From ab39541ef5da32a943336c055fe4c3e1b454875f Mon Sep 17 00:00:00 2001 From: PyBitmessage Translations Date: Wed, 1 Mar 2017 17:06:12 +0100 Subject: [PATCH 001/407] Auto-updated language ru from transifex --- src/translations/bitmessage_ru.qm | Bin 89972 -> 90153 bytes src/translations/bitmessage_ru.ts | 257 +++++++++++++++--------------- 2 files changed, 131 insertions(+), 126 deletions(-) diff --git a/src/translations/bitmessage_ru.qm b/src/translations/bitmessage_ru.qm index d14f75ae2b0b208dfdfe7a3fec7f4a548b711453..9b7a09a9afb9c2cca2b14ccdf26cd57d8714c0cc 100644 GIT binary patch delta 3727 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 F{{^oGP$B>T delta 3590 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 sYj=!x4AqA_8=5vq(oedxb EmailGatewayRegistrationDialog - + Registration failed: Регистрация не удалась: - + The requested email address is not available, please try a new one. Fill out the new desired email address (including @mailchuck.com) below: Запрашиваемый адрес email недоступен, попробуйте ввести другой. Введите желаемый адрес (включая @mailchuck.com) ниже: @@ -76,7 +76,7 @@ Email gateway allows you to communicate with email users. Currently, only the Mailchuck email gateway (@mailchuck.com) is available. Please type the desired email address (including @mailchuck.com) below: - Email-шлюз позволяет вам обмениваться сообщениями с пользователями обычной электронной почты. В настоящий момент доступен только 1 шлюз Mailchuck (@mailchuck.com). + Email-шлюз позволяет вам обмениваться сообщениями с пользователями обычной электронной почты. В настоящий момент доступен только шлюз Mailchuck (@mailchuck.com). Пожалуйста, введите желаемый адрес email (включая @mailchuck.com) ниже: @@ -318,7 +318,7 @@ Please type the desired email address (including @mailchuck.com) below: Сообщение доставлено в %1 - + Broadcast queued. Рассылка ожидает очереди. @@ -348,7 +348,7 @@ Please type the desired email address (including @mailchuck.com) below: Неизвестный статус: %1 %2 - + Not Connected Не соединено @@ -520,17 +520,17 @@ It is important that you back up this file. Would you like to open the file now? Соединение потеряно - + Connected Соединено - + Message trashed Сообщение удалено - + The TTL, or Time-To-Live is the length of time that the network will hold the message. The recipient must get it during this time. If your Bitmessage client does not hear an acknowledgement, it will resend the message automatically. The longer the Time-To-Live, the @@ -541,17 +541,17 @@ It is important that you back up this file. Would you like to open the file now? сообщение. Часто разумным вариантом будет установка TTL на 4 или 5 дней. - + Message too long Сообщение слишком длинное - + The message that you are trying to send is too long by %1 bytes. (The maximum is 261644 bytes). Please cut it down before sending. - Сообщение, которое вы пытаетесь отправить, длиннее максимально допустимого на %1 байт. (Максимально допустимое значение 261644 байт). Пожалуйста, сократите сообщение перед отправкой. + Сообщение, которое вы пытаетесь отправить, длиннее максимально допустимого на %1 байт. (Максимально допустимое значение 261644 байта). Пожалуйста, сократите сообщение перед отправкой. - + Error: Your account wasn't registered at an email gateway. Sending registration now as %1, please wait for the registration to be processed before retrying sending. Ошибка: ваш аккаунт не зарегистрирован на Email-шлюзе. Отправка регистрации %1, пожалуйста, подождите пока процесс регистрации не завершится, прежде чем попытаться отправить сообщение заново. @@ -596,67 +596,67 @@ It is important that you back up this file. Would you like to open the file now? - + Error: You must specify a From address. If you don't have one, go to the 'Your Identities' tab. Вы должны указать адрес в поле "От кого". Вы можете найти Ваш адрес во вкладе "Ваши Адреса". - + Address version number Версия адреса - + Concerning the address %1, Bitmessage cannot understand address version numbers of %2. Perhaps upgrade Bitmessage to the latest version. По поводу адреса %1: Bitmessage не поддерживаем адреса версии %2. Возможно, Вам нужно обновить клиент Bitmessage. - + Stream number Номер потока - + Concerning the address %1, Bitmessage cannot handle stream numbers of %2. Perhaps upgrade Bitmessage to the latest version. По поводу адреса %1: Bitmessage не поддерживаем стрим номер %2. Возможно, Вам нужно обновить клиент Bitmessage. - + Warning: You are currently not connected. Bitmessage will do the work necessary to send the message but it won't send until you connect. Внимание: Вы не подключены к сети. Bitmessage выполнит работу, требуемую для отправки сообщения, но не отправит его до тех пор, пока Вы не подключитесь. - + Message queued. Сообщение в очереди. - + Your 'To' field is empty. Вы не заполнили поле 'Кому'. - + Right click one or more entries in your address book and select 'Send message to this address'. Нажмите правую кнопку мыши на каком-либо адресе и выберите "Отправить сообщение на этот адрес". - + Fetched address from namecoin identity. Получить адрес через Namecoin. - + New Message Новое сообщение - + From От - + Sending email gateway registration request Отправка запроса на регистрацию на Email-шлюзе @@ -671,142 +671,142 @@ It is important that you back up this file. Would you like to open the file now? Вы ввели неправильный адрес. Это адрес проигнорирован. - + Error: You cannot add the same address to your address book twice. Try renaming the existing one if you want. Ошибка: Вы не можете добавлять один и тот же адрес в Адресную Книгу несколько раз. Попробуйте переименовать существующий адрес. - + Error: You cannot add the same address to your subscriptions twice. Perhaps rename the existing one if you want. Ошибка: вы не можете добавить один и тот же адрес в ваши подписки дважды. Пожалуйста, переименуйте имеющийся адрес, если хотите. - + Restart Перезапустить - + You must restart Bitmessage for the port number change to take effect. Вы должны перезапустить Bitmessage, чтобы смена номера порта имела эффект. - + Bitmessage will use your proxy from now on but you may want to manually restart Bitmessage now to close existing connections (if any). Bitmessage будет использовать Ваш прокси, начиная прямо сейчас. Тем не менее Вам имеет смысл перезапустить Bitmessage, чтобы закрыть уже существующие соединения. - + Number needed Требуется число - + Your maximum download and upload rate must be numbers. Ignoring what you typed. Скорости загрузки и выгрузки должны быть числами. Игнорирую то, что вы набрали. - + Will not resend ever Не пересылать никогда - + Note that the time limit you entered is less than the amount of time Bitmessage waits for the first resend attempt therefore your messages will never be resent. Обратите внимание, что лимит времени, который вы ввели, меньше чем время, которое Bitmessage ждет перед первой попыткой переотправки сообщения, поэтому ваши сообщения никогда не будут переотправлены. - + Sending email gateway unregistration request Отправка запроса на отмену регистрации на Email-шлюзе - + Sending email gateway status request Отправка запроса статуса аккаунта на Email-шлюзе - + Passphrase mismatch Секретная фраза не подходит - + The passphrase you entered twice doesn't match. Try again. Вы ввели две разные секретные фразы. Пожалуйста, повторите заново. - + Choose a passphrase Придумайте секретную фразу - + You really do need a passphrase. Вы действительно должны ввести секретную фразу. - + Address is gone Адрес утерян - + Bitmessage cannot find your address %1. Perhaps you removed it? Bitmessage не может найти Ваш адрес %1. Возможно Вы удалили его? - + Address disabled Адрес выключен - + Error: The address from which you are trying to send is disabled. You'll have to enable it on the 'Your Identities' tab before using it. Ошибка: адрес, с которого Вы пытаетесь отправить, выключен. Вам нужно будет включить этот адрес во вкладке "Ваши адреса". - + Entry added to the Address Book. Edit the label to your liking. Запись добавлена в Адресную Книгу. Вы можете её отредактировать. - + Entry added to the blacklist. Edit the label to your liking. Запись добавлена в чёрный список. Измените название по своему вкусу. - + Error: You cannot add the same address to your blacklist twice. Try renaming the existing one if you want. Ошибка: вы не можете добавить один и тот же адрес в чёрный список дважды. Попробуйте переименовать существующий адрес. - + Moved items to trash. Удалено в корзину. - + Undeleted item. Отменить удаление записи - + Save As... Сохранить как ... - + Write error. Ошибка записи. - + No addresses selected. Вы не выбрали адрес. - + If you delete the subscription, messages that you already received will become inaccessible. Maybe you can consider disabling the subscription instead. Disabled subscriptions will not receive new messages, but you can still view messages you already received. Are you sure you want to delete the subscription? @@ -815,7 +815,7 @@ Are you sure you want to delete the subscription? Вы уверены, что хотите отменить подписку? - + If you delete the channel, messages that you already received will become inaccessible. Maybe you can consider disabling the channel instead. Disabled channels will not receive new messages, but you can still view messages you already received. Are you sure you want to delete the channel? @@ -824,92 +824,92 @@ Are you sure you want to delete the channel? Вы уверены, что хотите удалить канал? - + Do you really want to remove this avatar? Вы уверены, что хотите удалить этот аватар? - + You have already set an avatar for this address. Do you really want to overwrite it? У вас уже есть аватар для этого адреса. Вы уверены, что хотите перезаписать аватар? - + Start-on-login not yet supported on your OS. Запуск программы при входе в систему ещё не поддерживается в вашей операционной системе. - + Minimize-to-tray not yet supported on your OS. Сворачивание в трей ещё не поддерживается в вашей операционной системе. - + Tray notifications not yet supported on your OS. Уведомления в трее ещё не поддерживаеются в вашей операционной системе. - + Testing... Проверяем... - + This is a chan address. You cannot use it as a pseudo-mailing list. Это адрес chan-а. Вы не можете его использовать как адрес рассылки. - + The address should start with ''BM-'' Адрес должен начинаться с "BM-" - + The address is not typed or copied correctly (the checksum failed). Адрес введен или скопирован неверно (контрольная сумма не сходится). - + The version number of this address is higher than this software can support. Please upgrade Bitmessage. Версия этого адреса более поздняя, чем Ваша программа. Пожалуйста, обновите программу Bitmessage. - + The address contains invalid characters. Адрес содержит запрещённые символы. - + Some data encoded in the address is too short. Данные, закодированные в адресе, слишком короткие. - + Some data encoded in the address is too long. Данные, закодированные в адресе, слишком длинные. - + Some data encoded in the address is malformed. Данные, закодированные в адресе, имеют неверный формат. - + Enter an address above. Введите адрес выше. - + Address is an old type. We cannot display its past broadcasts. Адрес старого типа. Мы не можем отобразить его прошлые рассылки. - + There are no recent broadcasts from this address to display. Нет недавних рассылок с этого адреса для отображения. - + You are using TCP port %1. (This can be changed in the settings). Вы используете TCP порт %1. (Его можно поменять в настройках). @@ -1006,7 +1006,7 @@ Are you sure you want to delete the channel? Send ordinary Message - Отправить обыкновенное сообщение + Отправить обычное сообщение @@ -1119,47 +1119,47 @@ Are you sure you want to delete the channel? Добавить новую запись - + Display the %1 recent broadcast(s) from this address. Показать %1 прошлых рассылок с этого адреса. - + New version of PyBitmessage is available: %1. Download it from https://github.com/Bitmessage/PyBitmessage/releases/latest Доступна новая версия PyBitmessage: %1. Загрузите её: https://github.com/Bitmessage/PyBitmessage/releases/latest - + Waiting for PoW to finish... %1% Ожидание окончания PoW... %1% - + Shutting down Pybitmessage... %1% Завершение PyBitmessage... %1% - + Waiting for objects to be sent... %1% Ожидание отправки объектов... %1% - + Saving settings... %1% Сохранение настроек... %1% - + Shutting down core... %1% Завершение работы ядра... %1% - + Stopping notifications... %1% Остановка сервиса уведомлений... %1% - + Shutdown imminent... %1% Завершение вот-вот произойдет... %1% @@ -1174,7 +1174,7 @@ Are you sure you want to delete the channel? %n день%n дня%n дней%n дней - + Shutting down PyBitmessage... %1% Завершение PyBitmessage... %1% @@ -1214,71 +1214,71 @@ Are you sure you want to delete the channel? - + Disk full Диск переполнен - + Alert: Your disk or data storage volume is full. Bitmessage will now exit. Внимание: свободное место на диске закончилось. Bitmessage завершит свою работу. - + Error! Could not find sender address (your address) in the keys.dat file. Ошибка: невозможно найти адрес отправителя (ваш адрес) в файле ключей keys.dat - + Doing work necessary to send broadcast... Выполнение работы, требуемой для рассылки... - + Broadcast sent on %1 Рассылка отправлена на %1 - + Encryption key was requested earlier. Ключ шифрования запрошен ранее. - + Sending a request for the recipient's encryption key. Отправка запроса ключа шифрования получателя. - + Looking up the receiver's public key Поиск открытого ключа получателя - + Problem: Destination is a mobile device who requests that the destination be included in the message but this is disallowed in your settings. %1 Проблема: адресат является мобильным устройством, которое требует, чтобы адрес назначения был включен в сообщение, однако, это запрещено в ваших настройках. %1 - + Doing work necessary to send message. There is no required difficulty for version 2 addresses like this. Выполнение работы, требуемой для отправки сообщения. Для адреса версии 2 (как этот), не требуется указание сложности. - + Doing work necessary to send message. Receiver's required difficulty: %1 and %2 Выполнение работы, требуемой для отправки сообщения. Получатель запросил сложность: %1 и %2 - + Problem: The work demanded by the recipient (%1 and %2) is more difficult than you are willing to do. %3 Проблема: сложность, затребованная получателем (%1 и %2) гораздо больше, чем вы готовы сделать. %3 - + Problem: You are trying to send a message to yourself or a chan but your encryption key could not be found in the keys.dat file. Could not encrypt message. %1 Проблема: вы пытаетесь отправить сообщение самому себе или в чан, но ваш ключ шифрования не найден в файле ключей keys.dat. Невозможно зашифровать сообщение. %1 @@ -1288,7 +1288,7 @@ Receiver's required difficulty: %1 and %2 Выполнение работы, требуемой для отправки сообщения. - + Message sent. Waiting for acknowledgement. Sent on %1 Сообщение отправлено. Ожидаем подтверждения. Отправлено на %1 @@ -1298,12 +1298,12 @@ Receiver's required difficulty: %1 and %2 Выполнение работы, требуемой для запроса ключа шифрования. - + Broadcasting the public key request. This program will auto-retry if they are offline. Рассылка запросов открытого ключа шифрования. Программа будет повторять попытки, если они оффлайн. - + Sending public key request. Waiting for reply. Requested at %1 Отправка запроса открытого ключа шифрования. Ожидание ответа. Запрошено в %1 @@ -1313,7 +1313,7 @@ Receiver's required difficulty: %1 and %2 Распределение портов UPnP завершилось выделением порта %1 - + UPnP port mapping removed Распределение портов UPnP отменено @@ -1323,7 +1323,7 @@ Receiver's required difficulty: %1 and %2 Отметить все сообщения прочтенные - + Are you sure you would like to mark all messages read? Вы уверены, что хотите отметить все сообщения как прочтенные? @@ -1333,37 +1333,37 @@ Receiver's required difficulty: %1 and %2 Выполнение работы, требуемой для отправки рассылки. - + Proof of work pending Ожидается доказательство работы - + %n object(s) pending proof of work - %n объект в ожидании подтверждения работы%n объекта в ожидании подтверждения работы%n объектов в ожидании подтверждения работы%n объектов в ожидании подтверждения работы + %n объект в ожидании доказательства работы%n объекта в ожидании доказательства работы%n объектов в ожидании доказательства работы%n объектов в ожидании доказательства работы - + %n object(s) waiting to be distributed %n объект ожидает раздачи%n объекта ожидают раздачи%n объектов ожидают раздачи%n объектов ожидают раздачи - + Wait until these tasks finish? Подождать завершения этих задач? - + Problem communicating with proxy: %1. Please check your network settings. Проблема коммуникации с прокси: %1. Пожалуйста, проверьте ваши сетевые настройки. - + SOCKS5 Authentication problem: %1. Please check your SOCKS5 settings. Проблема аутентификации SOCKS5: %1. Пожалуйста, проверьте настройки SOCKS5. - + The time on your computer, %1, may be wrong. Please verify your settings. Время на компьютере, %1, возможно неправильное. Пожалуйста, проверьте ваши настройки. @@ -1378,72 +1378,77 @@ Receiver's required difficulty: %1 and %2 не рекомендовано для чанов - + + Problems connecting? Try enabling UPnP in the Network Settings + Проблемы подключения? Попробуйте включить UPnP в сетевых настройках. + + + Error: Bitmessage addresses start with BM- Please check the recipient address %1 Ошибка: адреса Bitmessage начинаются с "BM-". Пожалуйста, проверьте адрес получателя %1. - + Error: The recipient address %1 is not typed or copied correctly. Please check it. Ошибка: адрес получателя %1 набран или скопирован неправильно. Пожалуйста, проверьте его. - + Error: The recipient address %1 contains invalid characters. Please check it. Ошибка: адрес получателя %1 содержит недопустимые символы. Пожалуйста, проверьте его. - + Error: The version of the recipient address %1 is too high. Either you need to upgrade your Bitmessage software or your acquaintance is being clever. Ошибка: версия адреса получателя %1 слишком высокая. Либо вам нужно обновить программу Bitmessage, либо ваш знакомый - умник. - + Error: Some data encoded in the recipient address %1 is too short. There might be something wrong with the software of your acquaintance. Ошибка: часть данных, закодированных в адресе получателя %1 слишком короткая. Видимо, что-то не так с программой, используемой вашим знакомым. - + Error: Some data encoded in the recipient address %1 is too long. There might be something wrong with the software of your acquaintance. Ошибка: часть данных, закодированных в адресе получателя %1 слишком длинная. Видимо, что-то не так с программой, используемой вашим знакомым. - + Error: Some data encoded in the recipient address %1 is malformed. There might be something wrong with the software of your acquaintance. Ошибка: часть данных, закодированных в адресе получателя %1 сформирована неправильно. Видимо, что-то не так с программой, используемой вашим знакомым. - + Error: Something is wrong with the recipient address %1. Ошибка: что-то не так с адресом получателя %1. - + Synchronisation pending Ожидается синхронизация - + Bitmessage hasn't synchronised with the network, %n object(s) to be downloaded. If you quit now, it may cause delivery delays. Wait until the synchronisation finishes? Bitmessage не синхронизирован с сетью, незагруженных объектов: %n. Выход сейчас может привести к задержкам доставки. Подождать завершения синхронизации?Bitmessage не синхронизирован с сетью, незагруженных объектов: %n. Выход сейчас может привести к задержкам доставки. Подождать завершения синхронизации?Bitmessage не синхронизирован с сетью, незагруженных объектов: %n. Выход сейчас может привести к задержкам доставки. Подождать завершения синхронизации?Bitmessage не синхронизирован с сетью, незагруженных объектов: %n. Выход сейчас может привести к задержкам доставки. Подождать завершения синхронизации? - + Not connected Не подключено - + Bitmessage isn't connected to the network. If you quit now, it may cause delivery delays. Wait until connected and the synchronisation finishes? Bitmessage не подключен к сети. Выход сейчас может привести к задержкам доставки. Подождать подключения и завершения синхронизации? - + Waiting for network connection... Ожидание сетевого подключения... - + Waiting for finishing synchronisation... Ожидание окончания синхронизации... @@ -1679,7 +1684,7 @@ The 'Random Number' option is selected by default but deterministic ad Use a Blacklist (Allow all incoming messages except those on the Blacklist) - Использовать чёрный список (Разрешить все входящие сообщения, кроме указанных в чёрном списке) + Использовать чёрный список (разрешить все входящие сообщения, кроме указанных в чёрном списке) @@ -2322,12 +2327,12 @@ The 'Random Number' option is selected by default but deterministic ad <html><head/><body><p>By default, if you send a message to someone and he is offline for more than two days, Bitmessage will send the message again after an additional two days. This will be continued with exponential backoff forever; messages will be resent after 5, 10, 20 days ect. until the receiver acknowledges them. Here you may change that behavior by having Bitmessage give up after a certain number of days or months.</p><p>Leave these input fields blank for the default behavior. </p></body></html> - <html><head/><body><p>По умолчанию, когда вы отправляете сообщение кому-либо, и адресат находится оффлайн несколько дней, ваш Bitmessage перепосылает сообщение. Это будет продолжаться с увеличивающимся по экспоненте интервалом; сообщение будет переотправляться, например, через 5, 10, 20 дней, пока адресат их запрашивает. Здесь вы можете поменять поведение по умолчанию, заставив Bitmessage отказываться от переотправки по прошествии указанного количества дней или месяцев.</p><p>Оставите поля пустыми, чтобы вернуться к поведению по умолчанию.</p></body></html> + <html><head/><body><p>По умолчанию, когда вы отправляете сообщение кому-либо, и адресат находится оффлайн несколько дней, ваш Bitmessage перепосылает сообщение. Это будет продолжаться с увеличивающимся по экспоненте интервалом; сообщение будет переотправляться, например, через 5, 10, 20 дней, пока адресат их запрашивает. Здесь вы можете изменить это поведение, заставив Bitmessage прекращать переотправку по прошествии указанного количества дней или месяцев.</p><p>Оставьте поля пустыми, чтобы вернуться к поведению по умолчанию.</p></body></html> Give up after - Отказ от переотправки через + Прекратить через From 405a06c08a914e0e09632ae605a72cdfbb982f3f Mon Sep 17 00:00:00 2001 From: Peter Surda Date: Thu, 2 Mar 2017 15:02:51 +0100 Subject: [PATCH 002/407] Indentation --- src/inventory.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/inventory.py b/src/inventory.py index 1087e655..f2e9b37b 100644 --- a/src/inventory.py +++ b/src/inventory.py @@ -303,7 +303,7 @@ class PendingUpload(object): self.hashes[objectHash]['sendCount'] += 1 self.hashes[objectHash]['peers'].remove(current_thread().peer) except KeyError: - pass + pass self.clearHashes(objectHash) def stop(self): From 53657dba47c070763b7624513cf3aa059fde1bc7 Mon Sep 17 00:00:00 2001 From: Peter Surda Date: Thu, 2 Mar 2017 15:03:08 +0100 Subject: [PATCH 003/407] Phase 1 of SHA256 support - new variable "digestalg" which defaults to "sha1", but allows "sha256" for those who want to sign using this - Addresses #953 --- src/highlevelcrypto.py | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/src/highlevelcrypto.py b/src/highlevelcrypto.py index 50f13cce..4a24fe20 100644 --- a/src/highlevelcrypto.py +++ b/src/highlevelcrypto.py @@ -1,4 +1,5 @@ from binascii import hexlify +from bmconfigparser import BMConfigParser import pyelliptic from pyelliptic import arithmetic as a, OpenSSL def makeCryptor(privkey): @@ -35,8 +36,17 @@ def sign(msg,hexPrivkey): # upgrade PyBitmessage gracefully. # https://github.com/yann2192/pyelliptic/pull/33 # More discussion: https://github.com/yann2192/pyelliptic/issues/32 - return makeCryptor(hexPrivkey).sign(msg, digest_alg=OpenSSL.digest_ecdsa_sha1) # SHA1 - #return makeCryptor(hexPrivkey).sign(msg, digest_alg=OpenSSL.EVP_sha256) # SHA256. We should switch to this eventually. + digestAlg = BMConfigParser().safeGet('bitmessagesettings', 'digestalg', 'sha1') + if digestAlg == "sha1": + # SHA1, this will eventually be deprecated + print "sha1" + return makeCryptor(hexPrivkey).sign(msg, digest_alg=OpenSSL.digest_ecdsa_sha1) + elif digestAlg == "sha256": + # SHA256. Eventually this will become the default + print "sha256" + return makeCryptor(hexPrivkey).sign(msg, digest_alg=OpenSSL.EVP_sha256) + else: + raise ValueError("Unknown digest algorithm %s" % (digestAlgo)) # Verifies with hex public key def verify(msg,sig,hexPubkey): # As mentioned above, we must upgrade gracefully to use SHA256. So From bea675f9a6057719ce22777a7671cd27d7381794 Mon Sep 17 00:00:00 2001 From: Peter Surda Date: Thu, 2 Mar 2017 15:09:01 +0100 Subject: [PATCH 004/407] Remove unnecessary "print" from previous commit --- src/highlevelcrypto.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/highlevelcrypto.py b/src/highlevelcrypto.py index 4a24fe20..5e7f549c 100644 --- a/src/highlevelcrypto.py +++ b/src/highlevelcrypto.py @@ -39,11 +39,9 @@ def sign(msg,hexPrivkey): digestAlg = BMConfigParser().safeGet('bitmessagesettings', 'digestalg', 'sha1') if digestAlg == "sha1": # SHA1, this will eventually be deprecated - print "sha1" return makeCryptor(hexPrivkey).sign(msg, digest_alg=OpenSSL.digest_ecdsa_sha1) elif digestAlg == "sha256": # SHA256. Eventually this will become the default - print "sha256" return makeCryptor(hexPrivkey).sign(msg, digest_alg=OpenSSL.EVP_sha256) else: raise ValueError("Unknown digest algorithm %s" % (digestAlgo)) From 983620640205d2bc82ef8e1b3181efeb416fcd97 Mon Sep 17 00:00:00 2001 From: Dmitri Bogomolov <4glitch@gmail.com> Date: Sun, 5 Mar 2017 23:07:18 +0200 Subject: [PATCH 005/407] Missed translations: namecoin and welcom message --- src/bitmessageqt/__init__.py | 6 ++---- src/translations/bitmessage.pro | 1 + 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/src/bitmessageqt/__init__.py b/src/bitmessageqt/__init__.py index fc9b4d9a..6b909164 100644 --- a/src/bitmessageqt/__init__.py +++ b/src/bitmessageqt/__init__.py @@ -659,14 +659,12 @@ class MyForm(settingsmixin.SMainWindow): self.rerenderTabTreeMessages() # Set welcome message - self.ui.textEditInboxMessage.setText( - """ + self.ui.textEditInboxMessage.setText(_translate("MainWindow", """ Welcome to easy and secure Bitmessage * send messages to other people * send broadcast messages like twitter or * discuss in chan(nel)s with other people - """ - ) + """)) # Initialize the address book self.rerenderAddressBook() diff --git a/src/translations/bitmessage.pro b/src/translations/bitmessage.pro index 22044d46..a4e9ff77 100644 --- a/src/translations/bitmessage.pro +++ b/src/translations/bitmessage.pro @@ -16,6 +16,7 @@ SOURCES = ../addresses.py\ ../helper_msgcoding.py\ ../helper_sent.py\ ../helper_startup.py\ + ../namecoin.py\ ../proofofwork.py\ ../shared.py\ ../upnp.py\ From ee7e630694c68ecc1c10a189bc796e0dc4665d04 Mon Sep 17 00:00:00 2001 From: Dmitri Bogomolov <4glitch@gmail.com> Date: Wed, 1 Mar 2017 16:46:13 +0200 Subject: [PATCH 006/407] Show a dialog with QR-code for selected bm-address --- setup.py | 12 +++- src/bitmessageqt/__init__.py | 14 ++++- src/plugins/__init__.py | 0 src/plugins/plugin.py | 12 ++++ src/plugins/qrcodeui.py | 101 ++++++++++++++++++++++++++++++++ src/translations/bitmessage.pro | 3 +- 6 files changed, 138 insertions(+), 4 deletions(-) create mode 100644 src/plugins/__init__.py create mode 100644 src/plugins/plugin.py create mode 100644 src/plugins/qrcodeui.py diff --git a/setup.py b/setup.py index 2c769a63..1a273eaf 100644 --- a/setup.py +++ b/setup.py @@ -191,6 +191,9 @@ if __name__ == "__main__": # TODO: add keywords #keywords='', install_requires=['msgpack-python'], + extras_require={ + 'qrcode': ['qrcode'] + }, classifiers=[ "License :: OSI Approved :: MIT License" "Operating System :: OS Independent", @@ -208,6 +211,7 @@ if __name__ == "__main__": 'pybitmessage.network', 'pybitmessage.pyelliptic', 'pybitmessage.socks', + 'pybitmessage.plugins' ], package_data={'': [ 'bitmessageqt/*.ui', 'bitmsghash/*.cl', 'sslkeys/*.pem', @@ -216,11 +220,15 @@ if __name__ == "__main__": ]}, ext_modules=[bitmsghash], zip_safe=False, - #entry_points={ + entry_points={ + 'gui.menu': [ + 'popMenuYourIdentities.qrcode = ' + 'pybitmessage.plugins.qrcodeui [qrcode]' + ], # 'console_scripts': [ # 'pybitmessage = pybitmessage.bitmessagemain:main' # ] - #}, + }, scripts=['src/pybitmessage'] ) except SystemExit: diff --git a/src/bitmessageqt/__init__.py b/src/bitmessageqt/__init__.py index 6b909164..feaf1c48 100644 --- a/src/bitmessageqt/__init__.py +++ b/src/bitmessageqt/__init__.py @@ -88,6 +88,12 @@ from statusbar import BMStatusBar import throttle from version import softwareVersion +try: + from plugins.plugin import get_plugins +except ImportError: + get_plugins = False + + def _translate(context, text, disambiguation = None, encoding = None, number = None): if number is None: return QtGui.QApplication.translate(context, text) @@ -3613,7 +3619,7 @@ class MyForm(settingsmixin.SMainWindow): address = self.getCurrentAccount() clipboard = QtGui.QApplication.clipboard() clipboard.setText(str(address)) - + def on_action_ClipboardMessagelist(self): tableWidget = self.getCurrentMessagelist() currentColumn = tableWidget.currentColumn() @@ -3741,6 +3747,12 @@ class MyForm(settingsmixin.SMainWindow): self.popMenuYourIdentities.addAction(self.actionEmailGateway) self.popMenuYourIdentities.addSeparator() self.popMenuYourIdentities.addAction(self.actionMarkAllRead) + + if get_plugins: + for plugin in get_plugins( + 'gui.menu', 'popMenuYourIdentities'): + plugin(self) + self.popMenuYourIdentities.exec_( self.ui.treeWidgetYourIdentities.mapToGlobal(point)) diff --git a/src/plugins/__init__.py b/src/plugins/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/src/plugins/plugin.py b/src/plugins/plugin.py new file mode 100644 index 00000000..288be48a --- /dev/null +++ b/src/plugins/plugin.py @@ -0,0 +1,12 @@ +# -*- coding: utf-8 -*- + +import pkg_resources + + +def get_plugins(group, point, name=None): + for plugin in pkg_resources.iter_entry_points(group): + if plugin.name.startswith(point): + try: + yield plugin.load().connect_plugin + except (AttributeError, pkg_resources.DistributionNotFound): + continue diff --git a/src/plugins/qrcodeui.py b/src/plugins/qrcodeui.py new file mode 100644 index 00000000..25b1e0b1 --- /dev/null +++ b/src/plugins/qrcodeui.py @@ -0,0 +1,101 @@ +# -*- coding: utf-8 -*- + +from PyQt4 import QtGui, QtCore +import qrcode + +from pybitmessage.tr import translateText + +try: + _fromUtf8 = QtCore.QString.fromUtf8 +except AttributeError: + _fromUtf8 = lambda s: s + + +# http://stackoverflow.com/questions/20452486 +class Image(qrcode.image.base.BaseImage): + def __init__(self, border, width, box_size): + self.border = border + self.width = width + self.box_size = box_size + size = (width + border * 2) * box_size + self._image = QtGui.QImage( + size, size, QtGui.QImage.Format_RGB16) + self._image.fill(QtCore.Qt.white) + + def pixmap(self): + return QtGui.QPixmap.fromImage(self._image) + + def drawrect(self, row, col): + painter = QtGui.QPainter(self._image) + painter.fillRect( + (col + self.border) * self.box_size, + (row + self.border) * self.box_size, + self.box_size, self.box_size, + QtCore.Qt.black) + + def save(self, stream, kind=None): + pass + + +class Ui_qrcodeDialog(object): + def setupUi(self, qrcodeDialog): + qrcodeDialog.setObjectName(_fromUtf8("qrcodeDialog")) + self.image = QtGui.QLabel(qrcodeDialog) + self.label = QtGui.QLabel(qrcodeDialog) + font = QtGui.QFont() + font.setBold(True) + font.setWeight(75) + self.label.setFont(font) + self.label.setAlignment( + QtCore.Qt.AlignCenter | QtCore.Qt.AlignVCenter) + self.buttonBox = QtGui.QDialogButtonBox(qrcodeDialog) + self.buttonBox.setOrientation(QtCore.Qt.Horizontal) + self.buttonBox.setStandardButtons(QtGui.QDialogButtonBox.Ok) + layout = QtGui.QVBoxLayout(qrcodeDialog) + layout.addWidget(self.image) + layout.addWidget(self.label) + layout.addWidget(self.buttonBox) + + self.retranslateUi(qrcodeDialog) + QtCore.QObject.connect(self.buttonBox, QtCore.SIGNAL( + _fromUtf8("accepted()")), qrcodeDialog.accept) + QtCore.QObject.connect(self.buttonBox, QtCore.SIGNAL( + _fromUtf8("rejected()")), qrcodeDialog.reject) + QtCore.QMetaObject.connectSlotsByName(qrcodeDialog) + + def retranslateUi(self, qrcodeDialog): + qrcodeDialog.setWindowTitle(QtGui.QApplication.translate( + "qrcodeDialog", "QR-code", + None, QtGui.QApplication.UnicodeUTF8 + )) + + def render(self, text): + self.label.setText(text) + self.image.setPixmap( + qrcode.make(text, image_factory=Image).pixmap()) + + +class qrcodeDialog(QtGui.QDialog): + + def __init__(self, parent): + QtGui.QWidget.__init__(self, parent) + self.ui = Ui_qrcodeDialog() + self.ui.setupUi(self) + self.parent = parent + QtGui.QWidget.resize(self, QtGui.QWidget.sizeHint(self)) + + +def connect_plugin(form): + def on_action_ShowQR(): + form.qrcodeDialogInstance = qrcodeDialog(form) + form.qrcodeDialogInstance.ui.render( + str(form.getCurrentAccount()) + ) + form.qrcodeDialogInstance.exec_() + + form.actionShowQRCode = \ + form.ui.addressContextMenuToolbarYourIdentities.addAction( + translateText("MainWindow", "Show QR-code"), + on_action_ShowQR + ) + form.popMenuYourIdentities.addAction(form.actionShowQRCode) diff --git a/src/translations/bitmessage.pro b/src/translations/bitmessage.pro index a4e9ff77..818d799d 100644 --- a/src/translations/bitmessage.pro +++ b/src/translations/bitmessage.pro @@ -41,7 +41,8 @@ SOURCES = ../addresses.py\ ../bitmessageqt/regenerateaddresses.py\ ../bitmessageqt/safehtmlparser.py\ ../bitmessageqt/settings.py\ - ../bitmessageqt/specialaddressbehavior.py + ../bitmessageqt/specialaddressbehavior.py\ + ../plugins/qrcodeui.py FORMS = \ ../bitmessageqt/blacklist.ui\ From bbf2264862a7b379b1b917c64c09f7f2225a8466 Mon Sep 17 00:00:00 2001 From: PyBitmessage Translations Date: Mon, 6 Mar 2017 20:54:39 +0100 Subject: [PATCH 007/407] Auto-updated language eo from transifex --- src/translations/bitmessage_eo.qm | Bin 84767 -> 86543 bytes src/translations/bitmessage_eo.ts | 430 +++++++++++++++++------------- 2 files changed, 246 insertions(+), 184 deletions(-) diff --git a/src/translations/bitmessage_eo.qm b/src/translations/bitmessage_eo.qm index 85a6ed578ae0028f9d2c7bcbf4265de7c0cfc68d..eddaf80806073c740d5119e830181d97b2877ddc 100644 GIT binary patch delta 5140 zcmb7I2UJwo*8a}S-04LDE0J79P!SMNEFf4wjctq-D>A|;BQT>32xxS~78MIRDuRd& zdlwZe))=3OSg?VTXspkI@u}GL-*+&H>%FY^*6&)*o;&xPy}!M`Z|`$-#S(6QK9}iY zdkw%JOzQ+l768L01Kf6Ctsn4>H_&(!(CqbT`(}X!?}c=u zm>e4*8_jezVE*pX#W+Krn&*os$rFhXq%!z{yt{{)>!QhT*+YpQ4fI%8w;?~z7^+Ki%A5q#bOpzeH$>nF6%I~ z6_8WJ^jBzq>#i(n5COmSH5&o)o4An0zNh_f`!KVu4P~A&jLjP63mkG~i+2hX^kd7m zj;B&Zv4i2 zQ?8<>?S3I@FNu7P=2PHrMLstLs}}jCOaZq3LuB)RO|H1BI#vR#D-p-Or^@%Rof4<{-URB65@(+) z0FEV!v)@MonHl1|$m!I6rQ!qXlZ3Rk_)Hc(k6tYEIv0=4|K0CzMa#R zv@lToFoH6lmoKhJodoROCw{$;M4VPC(bby=yl*bi4W};fo-e7nX%LC=!Cgs6Aq|wK zDoL20IKKQ+^37Qi;jpz5<91(~0}CZ_Dk|OmmXi3!<7qhUmZW?juFv~R(qCQ%HXW7B zY)PCZrArp(QrGx;OE%AEKu~MRmL>#reTZa>_gZ>3NeXgZs8W8C1Dis@)Pp3)9yfuF zHI0{?O!EN3M@p_Z9j6T!C1ppRlc+9Beo1iymapg7bzhnTyEvc4WbHe-R=+O=%=@@D zXJ{@|zU6{q#lS*4*YSlOXxWYHe)AHkUdKgFB5h>+#>GSswdt+6k%7lZL#do4k18FM z!%Yq-wt;<>$4#AV1Z(ce%?%O)G~$-FX#)J%kIQ*(1#|AgEt^RJr$6Af&Eg116t}xS z1vDgZ2S(BQ%vxM=2K95(-?)cedePh{;VQ*c5z{5^Rd{vkrVrBUqsdRpRB7L}Bx=nK zX;dO{|9cy0!bqE$X8J^_<#GVvwNRRzNp07Bh;-&S;%-2MG`HJburMEK{*80MxPj6Q zcH)eWl5TBEnSNU)EtuyFlns&=3Yxh0FX`!FS|Ip=^wK6L>YicJ@&QC)vpv!ZDaBd# zLgti76eay6Yo#nEE#%08Y=%a3NHml6ETtc&U62jPoCsW>B{TY)z`Q!krmkoQw9S*v z=uw9T%m>-Lz+Zr_gJhXCN&T<8%QElkfzG{Ui%TdY{S;YF+%_O$hit_vN+5KvY<1c+ znta1$o9Eo7Q5{)jJ?dB4awfAmUOe!X6?P{Fi)+b_2j+pb$(EfT5DCZ)vT`W}$a^mP z^>QGvv9;{JUf}ju*(;AC8d!_ut)>ZYkR0Rzsud2=Az9T`dSu*oG*2;1jUDxnj^KB@K>Pg;h@gB8wF1 zZ$8o)uvamsD+La?tgu&;&|wv>SoEYh(DAGyN2tg`gCch?Ir{iQv3%-3faAv%E8CL4 z`+16;t4NH74T`e?b-@(N6=y$CW#wlS=WbIwF3>A3Ue#0oZ#<&7vVblc!K)Py`n?C5 zzgH@4ZD_JRQmV#M7ob?_+><7ms#NK0ET%Kyow5PD12njx^qfjmJRYwcFl!67*8ycr z4wbaQJf-n*4-#F9GB$<+wtS}?eUpG*z3i~eDOEXX=tD5~>B^k@@9F$Mc0;)>^es)e za%DjTWxPqR+^>2;nTeD~i-pYUDNjtMeqH!fd42|oG3KfA*3i4a(QC>lSp=+M4HfIT zjSihMl_HJ0AUIy76khx^(YsRB&bbco&q|fP+iXH!!(mzV?JE5N0+=^U)%(*%%CM#?dID+Z z<1$so_^D)#s>~y01k_KJQxXQ|bX1kAXhE8Kt2#Z|1nj)4I{g~~&2Fx`nym+`VNjK( z*($&SZ>!3_SqyjKetxx8_1KBp@^LHGW1a>}gKV{*&<*nktJ`~1DX*of!}o}R z@h{Z_C&tlf*FYWfayXp{ZPmsldBAC@I&N!6DshN%lZP&Z^S}(P&=R zS)K7iCW+Kly~x&%#L@PydeMk(H2Zn=#?CxoXFAFcJ;0h;&gqgdY8}*t5xcK z7E*874fR20rO~?8VOjHH^^ql1iHSya@%>3aNL%&!(R$!lXY~bL1u%BAy7Z-R7v$B~ zCzMdU2=$BaiK^)nG&cFYJ#^~1Yh2?=ROe1;8b+U>Tkl1UXEp^eP0{$+Hvu;@HGZ=X zlDda#+Ef-1cZW4?okD>wzMAksUFqh0T+=`JBM{a~6X|OP`qt45-;hm}E7!zT5}*UW zYRtRo_rq$9#TG#csx;%n=95-jY?|-BBS&w3)8t(%27aiixpKyr&i5gjJM%|Sn;g}= zzDfYt6V030_H?(Kro~aBOuJhvudSzhgRj;l-h<}Dbgg?wdOqT;^{PRBV&`eS^~*?X zeYM|I&L*xk+VMXhqbq$&?F_aJn0j41|0%7@w}oo6cWx(_7l&W+Y0W%DSJavN!Uo?ES0SQa#4IGsQJWmlb>B;+vOw1>pDMDyNH=&# z5%4cRT~y9A8o7&g=J^DKU!oi9O_{#ku1oJs4#4Z?m+b^nRl0n)Qp)_kZqwP1U^Or5 zwsp}0x1)9YzRsh6pm4f_Nfp56!Mfu$$gT|1T{P=a8{vy}cSES3zYoy8ojZ*V85x~C z;UDZ#zC8-G8n=3(EEGy~Bw%KYgONUkZ#)c$r_YgOqhX@&26~F4&(UP9v@Qt>zZiLW(Ks0@ZluWLLph6JgT>@Dv46Gh}9I(x_&apx+ zkSwLijB`r<^PdP~e6%IO$R}BNqrsZS8_Y4h)fkvFM3al zKEJXGJ1t4E#zcOM(K06fZ`Y1Uv>0Nd4c4TpU42Fwt$e&Gj-sZRl9FhvCGp=v#F(tn z$yO_GGV{@~26JPxF}{hFr`554AI!eW$ECo>%df7kHT0CfP*saiPkZkUwSx5Mi74lITEH#Lk{lmnZj{6)n`4ZL)+C_<`krAHb5f!u-ku&>t6+Hef;w4KWX!2R z6dA5$=7KJwf7z#gTFnKhi?- z5}ZZ{{pr-;Xw_&ctBJ7tI0iwIgSU~NwIU1&QVfj?fypL+iS~~W0+0OIWMg6)Khj`| zr;0ZA@NXh?!+?PQcbvb}_-6|L;ol*U9qa0AS*m?+Wg~?{VT3QKpxT~YjMcW^s_SC! zc`&fXA5$%n1SwDvM`W1n>4)3cKkQ4lmln=r_x|_Vf)D$GMXde*Txhp9+uzHca%d}4 z|M#^8>4)>ZIU4CKp}_0m6fJYHZ+)HMEGQ?ADsLvR0#N#9Z}KL>ek53B&v>(*6NX8m zz1iE6s4u4Ns6EqbGmW5il0LCc@q$a?qjiMGTTg-%tEO%%R{n+)-mQH5PxSiab9 zFW-=L_bcXBxU)8UC}J10a$Q-FgehRky5!2-nYpmil__f1=|@9LD4WpB37>V$QzTwXJXyD*{!GpiV%b*ddpw%^*|QrNvc6N%Z)LVXw;%j~mjD+_-NX9K1G2hJ0@ AVE_OC delta 3789 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 EmailGatewayRegistrationDialog - + Registration failed: Registrado malsukcesis: - + The requested email address is not available, please try a new one. Fill out the new desired email address (including @mailchuck.com) below: La dezirata retpoŝtadreso ne estas disponebla, bonvolu provi alian. Entajpu novan deziratan adreson (kune kun @mailchuck.com) sube: @@ -166,52 +166,52 @@ Bonvolu entajpi deziratan retpoŝtadreson (kune kun @mailchuck.com) sube: MainWindow - + Reply to sender Respondi al sendinto - + Reply to channel Respondi al kanalo - + Add sender to your Address Book Aldoni sendinton al via adresaro - + Add sender to your Blacklist Aldoni sendinton al via nigra listo - + Move to Trash Movi al rubujo - + Undelete Malforviŝi - + View HTML code as formatted text Montri HTML-n kiel aranĝitan tekston - + Save message as... Konservi mesaĝon kiel... - + Mark Unread Marki kiel nelegitan - + New Nova @@ -236,12 +236,12 @@ Bonvolu entajpi deziratan retpoŝtadreson (kune kun @mailchuck.com) sube:Kopii adreson al tondejo - + Special address behavior... Speciala sinteno de adreso... - + Email gateway Retpoŝta kluzo @@ -251,37 +251,37 @@ Bonvolu entajpi deziratan retpoŝtadreson (kune kun @mailchuck.com) sube:Forigi - + Send message to this address Sendi mesaĝon al tiu adreso - + Subscribe to this address Aboni tiun adreson - + Add New Address Aldoni novan adreson - + Copy destination address to clipboard Kopii cel-adreson al tondejo - + Force send Devigi sendadon - + One of your addresses, %1, is an old version 1 address. Version 1 addresses are no longer supported. May we delete it now? Iu de viaj adresoj, %1, estas malnova versio 1 adreso. Versioj 1 adresoj ne estas jam subtenataj. Ĉu ni povas forviŝi ĝin? - + Waiting for their encryption key. Will request it again soon. Atendado je ilia ĉifroŝlosilo. Baldaŭ petos ĝin denove. @@ -291,17 +291,17 @@ Bonvolu entajpi deziratan retpoŝtadreson (kune kun @mailchuck.com) sube: - + Queued. En atendovico. - + Message sent. Waiting for acknowledgement. Sent at %1 Mesaĝo sendita. Atendado je konfirmo. Sendita je %1 - + Message sent. Sent at %1 Mesaĝo sendita. Sendita je %1 @@ -311,47 +311,47 @@ Bonvolu entajpi deziratan retpoŝtadreson (kune kun @mailchuck.com) sube: - + Acknowledgement of the message received %1 Ricevis konfirmon de la mesaĝo je %1 - + Broadcast queued. Elsendo en atendovico. - + Broadcast on %1 Elsendo je %1 - + Problem: The work demanded by the recipient is more difficult than you are willing to do. %1 Problemo: la demandita laboro de la ricevonto estas pli malfacila ol vi pretas fari. %1 - + Problem: The recipient's encryption key is no good. Could not encrypt message. %1 Problemo: la ĉifroŝlosilo de la ricevonto estas rompita. Ne povis ĉifri la mesaĝon. %1 - + Forced difficulty override. Send should start soon. Devigita superado de limito de malfacilaĵo. Sendado devus baldaŭ komenci. - + Unknown status: %1 %2 Nekonata stato: %1 %2 - + Not Connected Ne konektita - + Show Bitmessage Montri Bitmesaĝon @@ -361,12 +361,12 @@ Bonvolu entajpi deziratan retpoŝtadreson (kune kun @mailchuck.com) sube:Sendi - + Subscribe Aboni - + Channel Kanalo @@ -376,70 +376,70 @@ Bonvolu entajpi deziratan retpoŝtadreson (kune kun @mailchuck.com) sube:Eliri - + You may manage your keys by editing the keys.dat file stored in the same directory as this program. It is important that you back up this file. - Vi povas administri viajn ŝlosilojn redaktante la dosieron keys.dat en la sama dosierujo kiel tiu programo. Estas grava, ke vi faru sekurkopion de tiu dosiero. + Vi povas administri viajn ŝlosilojn per redakti la dosieron keys.dat en la sama dosierujo kiel tiu programo. Estas grava, ke vi faru sekurkopion de tiu dosiero. - + You may manage your keys by editing the keys.dat file stored in %1 It is important that you back up this file. - Vi povas administri viajn ŝlosilojn redaktante la dosieron keys.dat en la dosierujo + Vi povas administri viajn ŝlosilojn per redakti la dosieron keys.dat en la dosierujo %1. Estas grava, ke vi faru sekurkopion de tiu dosiero. - + Open keys.dat? Ĉu malfermi keys.dat? - + You may manage your keys by editing the keys.dat file stored in the same directory as this program. It is important that you back up this file. Would you like to open the file now? (Be sure to close Bitmessage before making any changes.) - Vi povas administri viajn ŝlosilojn redaktante la dosieron keys.dat en la sama dosierujo kiel tiu programo. Estas grava ke vi faru sekurkopion de tiu dosiero. Ĉu vi volas malfermi la dosieron nun? (Bonvolu certigi ke Bitmesaĝo estas fermita antaŭ fari ŝanĝojn.) + Vi povas administri viajn ŝlosilojn per redakti la dosieron keys.dat en la sama dosierujo kiel tiu programo. Estas grava ke vi faru sekurkopion de tiu dosiero. Ĉu vi volas malfermi la dosieron nun? (Bonvolu certigi ke Bitmesaĝo estas fermita antaŭ fari ŝanĝojn.) - + You may manage your keys by editing the keys.dat file stored in %1 It is important that you back up this file. Would you like to open the file now? (Be sure to close Bitmessage before making any changes.) - Vi povas administri viajn ŝlosilojn redaktante la dosieron keys.dat en la dosierujo + Vi povas administri viajn ŝlosilojn per redakti la dosieron keys.dat en la dosierujo %1. Estas grava, ke vi faru sekurkopion de tiu dosiero. Ĉu vi volas malfermi la dosieron nun? (Bonvolu certigi ke Bitmesaĝo estas fermita antaŭ fari ŝanĝojn.) - + Delete trash? Ĉu malplenigi rubujon? - + Are you sure you want to delete all trashed messages? Ĉu vi certe volas forviŝi ĉiujn mesaĝojn el la rubujo? - + bad passphrase malprava pasvorto - + You must type your passphrase. If you don't have one then this is not the form for you. Vi devas tajpi vian pasvorton. Se vi ne havas pasvorton, tiu ĉi ne estas la prava formularo por vi. - + Bad address version number Erara numero de adresversio - + Your address version number must be a number: either 3 or 4. Via numero de adresversio devas esti: aŭ 3 aŭ 4. - + Your address version number must be either 3 or 4. Via numero de adresversio devas esti: aŭ 3 aŭ 4. @@ -509,40 +509,40 @@ Estas grava, ke vi faru sekurkopion de tiu dosiero. Ĉu vi volas malfermi la dos - + Connection lost Perdis konekton - + Connected Konektita - + Message trashed Movis mesaĝon al rubujo - + The TTL, or Time-To-Live is the length of time that the network will hold the message. The recipient must get it during this time. If your Bitmessage client does not hear an acknowledgement, it will resend the message automatically. The longer the Time-To-Live, the more work your computer must do to send the message. A Time-To-Live of four or five days is often appropriate. - La vivdaŭro signifas ĝis kiam la reto tenos la mesaĝon. La ricevonto devos elŝuti ĝin dum tiu tempo. Se via Bitmesaĝa kliento ne ricevos konfirmon, ĝi resendos mesaĝon aŭtomate. Ju pli longa vivdaŭro, des pli laboron via komputilo bezonos fari por sendi mesaĝon. Vivdaŭro proksimume kvin aŭ kvar horoj estas ofte konvena. + La vivdaŭro signifas ĝis kiam la reto tenos la mesaĝon. La ricevonto devos elŝuti ĝin dum tiu tempo. Se via bitmesaĝa kliento ne ricevos konfirmon, ĝi resendos mesaĝon aŭtomate. Ju pli longa vivdaŭro, des pli laboron via komputilo bezonos fari por sendi mesaĝon. Vivdaŭro proksimume kvin aŭ kvar horoj estas ofte konvena. - + Message too long Mesaĝo tro longa - + The message that you are trying to send is too long by %1 bytes. (The maximum is 261644 bytes). Please cut it down before sending. La mesaĝon kiun vi provis sendi estas tro longa je %1 bitokoj. (La maksimumo estas 261644 bitokoj.) Bonvolu mallongigi ĝin antaŭ sendado. - + Error: Your account wasn't registered at an email gateway. Sending registration now as %1, please wait for the registration to be processed before retrying sending. Eraro: Via konto ne estas registrita je retpoŝta kluzo. Registranta nun kiel %1, bonvolu atendi ĝis la registrado finos antaŭ vi reprovos sendi iun ajn. @@ -587,67 +587,67 @@ Estas grava, ke vi faru sekurkopion de tiu dosiero. Ĉu vi volas malfermi la dos - + Error: You must specify a From address. If you don't have one, go to the 'Your Identities' tab. Eraro: Vi devas elekti sendontan adreson. Se vi ne havas iun, iru al langeto 'Viaj identigoj'. - + Address version number Numero de adresversio - + Concerning the address %1, Bitmessage cannot understand address version numbers of %2. Perhaps upgrade Bitmessage to the latest version. Dum prilaborado de adreso adreso %1, Bitmesaĝo ne povas kompreni numerojn %2 de adresversioj. Eble ĝisdatigu Bitmesaĝon al la plej nova versio. - + Stream number Fluo numero - + Concerning the address %1, Bitmessage cannot handle stream numbers of %2. Perhaps upgrade Bitmessage to the latest version. Dum prilaborado de adreso %1, Bitmesaĝo ne povas priservi %2 fluojn numerojn. Eble ĝisdatigu Bitmesaĝon al la plej nova versio. - + Warning: You are currently not connected. Bitmessage will do the work necessary to send the message but it won't send until you connect. Atentu: Vi ne estas nun konektita. Bitmesaĝo faros necesan laboron por sendi mesaĝon, tamen ĝi ne sendos ĝin antaŭ vi konektos. - + Message queued. Mesaĝo envicigita. - + Your 'To' field is empty. Via "Ricevonto"-kampo malplenas. - + Right click one or more entries in your address book and select 'Send message to this address'. Dekstre alklaku kelka(j)n elemento(j)n en via adresaro kaj elektu 'Sendi mesaĝon al tiu adreso'. - + Fetched address from namecoin identity. - Venigis adreson de Namecoin-a identigo. + Venigis adreson de namecoin-a identigo. - + New Message Nova mesaĝo - + From De - + Sending email gateway registration request Sendado de peto pri registrado je retpoŝta kluzo @@ -662,142 +662,142 @@ Estas grava, ke vi faru sekurkopion de tiu dosiero. Ĉu vi volas malfermi la dos La adreso kiun vi enmetis estas malĝusta. Ignoras ĝin. - + Error: You cannot add the same address to your address book twice. Try renaming the existing one if you want. Eraro: Vi ne povas duoble aldoni la saman adreson al via adresaro. Provu renomi la ekzistan se vi volas. - + Error: You cannot add the same address to your subscriptions twice. Perhaps rename the existing one if you want. Eraro: Vi ne povas aldoni duoble la saman adreson al viaj abonoj. Eble renomi la ekzistan se vi volas. - + Restart Restartigi - + You must restart Bitmessage for the port number change to take effect. Vi devas restartigi Bitmesaĝon por ke la ŝanĝo de la numero de pordo (Port Number) efektivigu. - + Bitmessage will use your proxy from now on but you may want to manually restart Bitmessage now to close existing connections (if any). Bitmesaĝo uzos vian prokurilon (proxy) ekde nun, sed eble vi volas permane restartigi Bitmesaĝon nun, por ke ĝi fermu eblajn ekzistajn konektojn. - + Number needed Numero bezonata - + Your maximum download and upload rate must be numbers. Ignoring what you typed. Maksimumaj elŝutrapido kaj alŝutrapido devas esti numeroj. Ignoras kion vi enmetis. - + Will not resend ever Resendos neniam - + Note that the time limit you entered is less than the amount of time Bitmessage waits for the first resend attempt therefore your messages will never be resent. Rigardu, ke la templimon vi enmetis estas pli malgrandan ol tempo dum kiu Bitmesaĝo atendas por resendi unuafoje, do viaj mesaĝoj estos senditaj neniam. - + Sending email gateway unregistration request Sendado de peto pri malregistrado de retpoŝta kluzo - + Sending email gateway status request Sendado de peto pri stato de retpoŝta kluzo - + Passphrase mismatch Pasfrazoj malsamas - + The passphrase you entered twice doesn't match. Try again. La pasfrazo kiun vi duoble enmetis malsamas. Provu denove. - + Choose a passphrase Elektu pasfrazon - + You really do need a passphrase. Vi ja vere bezonas pasfrazon. - + Address is gone Adreso foriris - + Bitmessage cannot find your address %1. Perhaps you removed it? Bitmesaĝo ne povas trovi vian adreson %1. Ĉu eble vi forviŝis ĝin? - + Address disabled Adreso malŝaltita - + Error: The address from which you are trying to send is disabled. You'll have to enable it on the 'Your Identities' tab before using it. Eraro: La adreso kun kiu vi provas sendi estas malŝaltita. Vi devos ĝin ŝalti en la langeto 'Viaj identigoj' antaŭ uzi ĝin. - + Entry added to the Address Book. Edit the label to your liking. Aldonis elementon al adresaro. Redaktu la etikedon laŭvole. - + Entry added to the blacklist. Edit the label to your liking. Aldonis elementon al la nigra listo. Redaktu la etikedon laŭvole. - + Error: You cannot add the same address to your blacklist twice. Try renaming the existing one if you want. Eraro: Vi ne povas duoble aldoni la saman adreson al via nigra listo. Provu renomi la jaman se vi volas. - + Moved items to trash. Movis elementojn al rubujo. - + Undeleted item. Malforviŝis elementon. - + Save As... Konservi kiel... - + Write error. Skriberaro. - + No addresses selected. Neniu adreso elektita. - + If you delete the subscription, messages that you already received will become inaccessible. Maybe you can consider disabling the subscription instead. Disabled subscriptions will not receive new messages, but you can still view messages you already received. Are you sure you want to delete the subscription? @@ -806,7 +806,7 @@ Are you sure you want to delete the subscription? Ĉu vi certe volas forviŝi la abonon? - + If you delete the channel, messages that you already received will become inaccessible. Maybe you can consider disabling the channel instead. Disabled channels will not receive new messages, but you can still view messages you already received. Are you sure you want to delete the channel? @@ -815,94 +815,94 @@ Are you sure you want to delete the channel? Ĉu vi certe volas forviŝi la kanalon? - + Do you really want to remove this avatar? Ĉu vi certe volas forviŝi tiun ĉi avataron? - + You have already set an avatar for this address. Do you really want to overwrite it? Vi jam agordis avataron por tiu ĉi adreso. Ĉu vi vere volas superskribi ĝin? - + Start-on-login not yet supported on your OS. Starto-dum-ensaluto ne estas ankoraŭ ebla en via operaciumo. - + Minimize-to-tray not yet supported on your OS. Plejetigo al taskopleto ne estas ankoraŭ ebla en via operaciumo. - + Tray notifications not yet supported on your OS. Taskopletaj sciigoj ne estas ankoraŭ eblaj en via operaciumo. - + Testing... Testado... - + This is a chan address. You cannot use it as a pseudo-mailing list. Tio ĉi estas kanaladreso. Vi ne povas ĝin uzi kiel kvazaŭ-dissendolisto. - + The address should start with ''BM-'' La adreso komencu kun "BM-" - + The address is not typed or copied correctly (the checksum failed). La adreso ne estis prave tajpita aŭ kopiita (kontrolsumo malsukcesis). - + The version number of this address is higher than this software can support. Please upgrade Bitmessage. La numero de adresversio estas pli alta ol tiun, kiun la programo poveblas subteni. Bonvolu ĝisdatigi Bitmesaĝon. - + The address contains invalid characters. La adreso enhavas malpermesitajn simbolojn. - + Some data encoded in the address is too short. Kelkaj datumoj koditaj en la adreso estas tro mallongaj. - + Some data encoded in the address is too long. Kelkaj datumoj koditaj en la adreso estas tro longaj. - + Some data encoded in the address is malformed. Kelkaj datumoj koditaj en la adreso estas misformitaj. - + Enter an address above. Enmetu adreson supre. - + Address is an old type. We cannot display its past broadcasts. Malnova speco de adreso. Ne povas montri ĝiajn antaŭajn elsendojn. - + There are no recent broadcasts from this address to display. Neniaj lastatempaj elsendoj de tiu ĉi adreso por montri. - + You are using TCP port %1. (This can be changed in the settings). - Vi estas uzanta TCP pordo %1 (Tio ĉi estas ŝanĝebla en la agordoj). + Vi uzas TCP-pordon %1 (tio ĉi estas ŝanĝebla en la agordoj). @@ -1110,47 +1110,47 @@ Are you sure you want to delete the channel? Aldoni novan elementon - + Display the %1 recent broadcast(s) from this address. Montri la %1 lasta(j)n elsendo(j)n de tiu adreso. - + New version of PyBitmessage is available: %1. Download it from https://github.com/Bitmessage/PyBitmessage/releases/latest La nova versio de PyBitmessage estas disponebla: %1. Elŝutu ĝin de https://github.com/Bitmessage/PyBitmessage/releases/latest - + Waiting for PoW to finish... %1% Atendado ĝis laborpruvo finos... %1% - + Shutting down Pybitmessage... %1% Fermado de PyBitmessage... %1% - + Waiting for objects to be sent... %1% Atendado ĝis objektoj estos senditaj... %1% - + Saving settings... %1% Konservado de agordoj... %1% - + Shutting down core... %1% Fermado de kerno... %1% - + Stopping notifications... %1% Haltigado de sciigoj... %1% - + Shutdown imminent... %1% Fermado tuj... %1% @@ -1160,17 +1160,17 @@ Are you sure you want to delete the channel? %n horo%n horoj - + %n day(s) %n tago%n tagoj - + Shutting down PyBitmessage... %1% Fermado de PyBitmessage... %1% - + Sent Senditaj @@ -1215,86 +1215,86 @@ Are you sure you want to delete the channel? Atentu: Via disko aŭ subdisko estas plenplena. Bitmesaĝo fermiĝos. - + Error! Could not find sender address (your address) in the keys.dat file. Eraro! Ne povas trovi adreson de sendanto (vian adreson) en la dosiero keys.dat. - + Doing work necessary to send broadcast... Kalkulado de laborpruvo, kiu endas por sendi elsendon... - + Broadcast sent on %1 Elsendo sendita je %1 - + Encryption key was requested earlier. Peto pri ĉifroŝlosilo jam sendita. - + Sending a request for the recipient's encryption key. Sendado de peto pri ĉifroŝlosilo de ricevonto. - + Looking up the receiver's public key Serĉado de publika ĉifroŝlosilo de ricevonto - + Problem: Destination is a mobile device who requests that the destination be included in the message but this is disallowed in your settings. %1 Eraro: celadreso estas portebla aparato kiu necesas, ke la celadreso estu enhavita en la mesaĝo, sed tio estas malpermesita ne viaj agordoj. %1 - + Doing work necessary to send message. There is no required difficulty for version 2 addresses like this. Kalkulado de laborpruvo, kiu endas por sendi mesaĝon. Malfacilaĵo ne estas bezonata por adresoj versioj 2, kiel tiu ĉi adreso. - + Doing work necessary to send message. Receiver's required difficulty: %1 and %2 Kalkulado de laborpruvo, kiu endas por sendi mesaĝon. Ricevonto postulas malfacilaĵon: %1 kaj %2 - + Problem: The work demanded by the recipient (%1 and %2) is more difficult than you are willing to do. %3 Eraro: la demandita laboro de la ricevonto (%1 kaj %2) estas pli malfacila ol vi pretas fari. %3 - + Problem: You are trying to send a message to yourself or a chan but your encryption key could not be found in the keys.dat file. Could not encrypt message. %1 Eraro: Vi provis sendi mesaĝon al vi mem aŭ al kanalo, tamen via ĉifroŝlosilo ne estas trovebla en la dosiero keys.dat. Mesaĝo ne povis esti ĉifrita. %1 - + Doing work necessary to send message. Kalkulado de laborpruvo, kiu endas por sendi mesaĝon. - + Message sent. Waiting for acknowledgement. Sent on %1 Mesaĝo sendita. Atendado je konfirmo. Sendita je %1 - + Doing work necessary to request encryption key. Kalkulado de laborpruvo, kiu endas por peti pri ĉifroŝlosilo. - + Broadcasting the public key request. This program will auto-retry if they are offline. Elsendado de peto pri publika ĉifroŝlosilo. La programo reprovos se ili estas eksterrete. - + Sending public key request. Waiting for reply. Requested at %1 Sendado de peto pri publika ĉifroŝlosilo. Atendado je respondo. Petis je %1 @@ -1304,142 +1304,196 @@ Ricevonto postulas malfacilaĵon: %1 kaj %2 UPnP pord-mapigo farita je pordo %1 - + UPnP port mapping removed UPnP pord-mapigo forigita - + Mark all messages as read Marki ĉiujn mesaĝojn kiel legitajn - + Are you sure you would like to mark all messages read? Ĉu vi certe volas marki ĉiujn mesaĝojn kiel legitajn? - + Doing work necessary to send broadcast. Kalkulado de laborpruvo, kiu endas por sendi elsendon. - + Proof of work pending Laborpruvo haltigita - + %n object(s) pending proof of work Haltigis laborpruvon por %n objektoHaltigis laborpruvon por %n objektoj - + %n object(s) waiting to be distributed %n objekto atendas je sendato%n objektoj atendas je sendato - + Wait until these tasks finish? Ĉu atendi ĝis tiujn taskojn finos? - + Problem communicating with proxy: %1. Please check your network settings. Eraro dum komunikado kun prokurilo: %1. Bonvolu kontroli viajn retajn agordojn. - + SOCKS5 Authentication problem: %1. Please check your SOCKS5 settings. Eraro dum SOCKS5 aŭtentigado: %1. Bonvolu kontroli viajn SOCKS5-agordojn. - + The time on your computer, %1, may be wrong. Please verify your settings. La horloĝo de via komputilo, %1, eble eraras. Bonvolu kontroli viajn agordojn. + + + The name %1 was not found. + La nomo %1 ne trovita. + + + + The namecoin query failed (%1) + La namecoin-peto fiaskis (%1) + + + + The namecoin query failed. + La namecoin-peto fiaskis. + + + + The name %1 has no valid JSON data. + La nomo %1 ne havas ĝustajn JSON-datumojn. + + + + The name %1 has no associated Bitmessage address. + La nomo %1 ne estas atribuita kun bitmesaĝa adreso. + + + + Success! Namecoind version %1 running. + Sukceso! Namecoind versio %1 funkcias. + + + + Success! NMControll is up and running. + Sukceso! NMControl funkcias ĝuste. + + + + Couldn't understand NMControl. + Ne povis kompreni NMControl. + Your GPU(s) did not calculate correctly, disabling OpenCL. Please report to the developers. Via(j) vidprocesoro(j) ne kalkulis senerare, malaktiviganta OpenCL. Bonvolu raporti tion al programistoj. - + + + Welcome to easy and secure Bitmessage + * send messages to other people + * send broadcast messages like twitter or + * discuss in chan(nel)s with other people + + +Bonvenon al facila kaj sekura Bitmesaĝo +* sendi mesaĝojn al aliaj homoj +* sendi elsendajn mesaĝojn (kiel per Tvitero) +* babili kun aliaj uloj en mesaĝ-kanaloj + + + not recommended for chans malkonsilinda por kanaloj - + Problems connecting? Try enabling UPnP in the Network Settings Ĉu problemo kun konektado? Provu aktivigi UPnP en retaj agordoj. - + Error: Bitmessage addresses start with BM- Please check the recipient address %1 Eraro: bitmesaĝaj adresoj komenciĝas kun BM-. Bonvolu kontroli la adreson de ricevonto %1 - + Error: The recipient address %1 is not typed or copied correctly. Please check it. Eraro: la adreso de ricevonto %1 estas malprave tajpita aŭ kopiita. Bonvolu kontroli ĝin. - + Error: The recipient address %1 contains invalid characters. Please check it. Eraro: la adreso de ricevonto %1 enhavas malpermesatajn simbolojn. Bonvolu kontroli ĝin. - + Error: The version of the recipient address %1 is too high. Either you need to upgrade your Bitmessage software or your acquaintance is being clever. - Eraro: la versio de adreso de ricevonto %1 estas tro alta. Eble vi devas ĝisdatigi vian Bitmesaĝan programon aŭ via sagaca konato uzas alian programon. + Eraro: la versio de adreso de ricevonto %1 estas tro alta. Eble vi devas ĝisdatigi vian bitmesaĝan programon aŭ via sagaca konato uzas alian programon. - + Error: Some data encoded in the recipient address %1 is too short. There might be something wrong with the software of your acquaintance. Eraro: kelkaj datumoj koditaj en la adreso de ricevonto %1 estas tro mallongaj. Povus esti ke io en la programo de via konato malfunkcias. - + Error: Some data encoded in the recipient address %1 is too long. There might be something wrong with the software of your acquaintance. Eraro: kelkaj datumoj koditaj en la adreso de ricevonto %1 estas tro longaj. Povus esti ke io en la programo de via konato malfunkcias. - + Error: Some data encoded in the recipient address %1 is malformed. There might be something wrong with the software of your acquaintance. Eraro: kelkaj datumoj koditaj en la adreso de ricevonto %1 estas misformitaj. Povus esti ke io en la programo de via konato malfunkcias. - + Error: Something is wrong with the recipient address %1. Eraro: io malĝustas kun la adreso de ricevonto %1. - + Synchronisation pending Samtempigado haltigita - + Bitmessage hasn't synchronised with the network, %n object(s) to be downloaded. If you quit now, it may cause delivery delays. Wait until the synchronisation finishes? Bitmesaĝo ne estas samtempigita kun la reto, %n objekto elŝutendas. Se vi eliros nun, tio povas igi malfruiĝojn de liveradoj. Ĉu atendi ĝis la samtempigado finiĝos?Bitmesaĝo ne estas samtempigita kun la reto, %n objektoj elŝutendas. Se vi eliros nun, tio povas igi malfruiĝojn de liveradoj. Ĉu atendi ĝis la samtempigado finiĝos? - + Not connected Nekonektita - + Bitmessage isn't connected to the network. If you quit now, it may cause delivery delays. Wait until connected and the synchronisation finishes? Bitmesaĝo ne estas konektita al la reto. Se vi eliros nun, tio povas igi malfruiĝojn de liveradoj. Ĉu atendi ĝis ĝi konektos kaj la samtempigado finiĝos? - + Waiting for network connection... Atendado je retkonekto... - + Waiting for finishing synchronisation... Atendado ĝis samtempigado finiĝos... @@ -1653,7 +1707,7 @@ La 'hazardnombra' adreso estas antaŭagordita, sed antaŭkalkuleblaj a <html><head/><body><p>Distributed under the MIT/X11 software license; see <a href="http://www.opensource.org/licenses/mit-license.php"><span style=" text-decoration: underline; color:#0000ff;">http://www.opensource.org/licenses/mit-license.php</span></a></p></body></html> - <html><head/><body><p>Distribuata sub la permesilo "MIT/X11 software license"; vidu <a href="http://www.opensource.org/licenses/mit-license.php"><span style=" text-decoration: underline; color:#0000ff;">http://www.opensource.org/licenses/mit-license.php</span></a></p></body></html> + <html><head/><body><p>Distribuata laŭ la permesilo "MIT/X11 software license"; vidu <a href="http://www.opensource.org/licenses/mit-license.php"><span style=" text-decoration: underline; color:#0000ff;">http://www.opensource.org/licenses/mit-license.php</span></a></p></body></html> @@ -1765,7 +1819,7 @@ La 'hazardnombra' adreso estas antaŭagordita, sed antaŭkalkuleblaj a You have made at least one connection to a peer using an outgoing connection but you have not yet received any incoming connections. Your firewall or home router probably isn't configured to forward incoming TCP connections to your computer. Bitmessage will work just fine but it would help the Bitmessage network if you allowed for incoming connections and will help you be a better-connected node. - Vi konektis almenaŭ al unu samtavolano uzante eliranta konekto, sed vi ankoraŭ ne ricevis enirantajn konetkojn. Via fajroŝirmilo (firewall) aŭ hejma enkursigilo (router) verŝajne estas agordita ne plusendi enirantajn TCP konektojn al via komputilo. Bitmesaĝo funkcios sufiĉe bone, sed helpus al la Bitmesaĝa reto se vi permesus enirantajn konektojn kaj tiel estus pli bone konektita nodo. + Vi konektis almenaŭ al unu samtavolano uzante elirantan konekton, sed vi ankoraŭ ne ricevis enirantajn konektojn. Via fajroŝirmilo (firewall) aŭ hejma enkursigilo (router) verŝajne estas agordita por ne plusendi enirantajn TCP konektojn al via komputilo. Bitmesaĝo funkcios sufiĉe bone, sed helpus al la bitmesaĝa reto se vi permesus enirantajn konektojn kaj tiel estus pli bone konektita nodo. @@ -2000,6 +2054,14 @@ La 'hazardnombra' adreso estas antaŭagordita, sed antaŭkalkuleblaj a C PoW modulo nedisponebla. Bonvolu konstrui ĝin. + + qrcodeDialog + + + QR-code + QR-kodo + + regenerateAddressesDialog @@ -2020,7 +2082,7 @@ La 'hazardnombra' adreso estas antaŭagordita, sed antaŭkalkuleblaj a Number of addresses to make based on your passphrase: - Kvanto de farotaj adresoj bazante sur via pasfrazo: + Nombro da farotaj adresoj bazante sur via pasfrazo: @@ -2045,7 +2107,7 @@ La 'hazardnombra' adreso estas antaŭagordita, sed antaŭkalkuleblaj a You must check (or not check) this box just like you did (or didn't) when you made your addresses the first time. - Vi devas marki (aŭ ne marki) tiun markobutono samkiel vi faris kiam vi generis vian adreson unuafoje. + Vi devas marki (aŭ ne marki) ĉi tiun markobutonon samkiel vi faris kiam vi generis vian adreson unuafoje. @@ -2229,7 +2291,7 @@ La 'hazardnombra' adreso estas antaŭagordita, sed antaŭkalkuleblaj a The 'Total difficulty' affects the absolute amount of work the sender must complete. Doubling this value doubles the amount of work. - La 'Tuta malfacilaĵo' efikas sur la tuta kvalito da laboro kiu la sendonto devos fari. Duobligo de tiu valoro, duobligas la kvanton de laboro. + La 'Tuta malfacilaĵo' efikas sur la tuta kvalito da laboro, kiun la sendonto devos fari. Duobligo de tiu valoro, duobligas la kvanton de laboro. @@ -2239,7 +2301,7 @@ La 'hazardnombra' adreso estas antaŭagordita, sed antaŭkalkuleblaj a When someone sends you a message, their computer must first complete some work. The difficulty of this work, by default, is 1. You may raise this default for new addresses you create by changing the values here. Any new addresses you create will require senders to meet the higher difficulty. There is one exception: if you add a friend or acquaintance to your address book, Bitmessage will automatically notify them when you next send a message that they need only complete the minimum amount of work: difficulty 1. - Kiam iu ajn sendas al vi mesaĝon, lia komputilo devas unue fari iom da laboro. La malfacilaĵo de tiu laboro implicite estas 1. Vi povas pligrandigi tiun valoron por novaj adresoj, kiuj vi generos per ŝanĝo de ĉi-tiaj valoroj. Ĉiuj novaj adresoj kreotaj de vi bezonos por ke sendontoj akceptu pli altan malfacilaĵon. Estas unu escepto: se vi aldonos kolegon al vi adresaro, Bitmesaĝo aŭtomate sciigos lin kiam vi sendos mesaĝon, ke li bezonos fari nur minimuman kvaliton da laboro: malfacilaĵo 1. + Kiam iu ajn sendas al vi mesaĝon, lia komputilo devas unue fari iom da laboro. La malfacilaĵo de tiu laboro implicite estas 1. Vi povas pligrandigi tiun valoron por novaj adresoj, kiujn vi generos per ŝanĝo de ĉi-tiaj valoroj. Ĉiuj novaj adresoj kreotaj de vi bezonos por ke sendontoj akceptu pli altan malfacilaĵon. Estas unu escepto: se vi aldonos kolegon al vi adresaro, Bitmesaĝo aŭtomate sciigos lin kiam vi sendos mesaĝon, ke li bezonos fari nur minimuman kvaliton da laboro: malfacilaĵo 1. @@ -2354,7 +2416,7 @@ La 'hazardnombra' adreso estas antaŭagordita, sed antaŭkalkuleblaj a Maximum outbound connections: [0: none] - Maksimume da eligaj konektoj: [0: none] + Maksimumo de eligaj konektoj: [0: senlima] From fe6f446d072263d87ea73216d279cb41f71bf271 Mon Sep 17 00:00:00 2001 From: PyBitmessage Translations Date: Mon, 6 Mar 2017 20:54:51 +0100 Subject: [PATCH 008/407] Auto-updated language pl from transifex --- src/translations/bitmessage_pl.qm | Bin 87912 -> 89786 bytes src/translations/bitmessage_pl.ts | 408 +++++++++++++++++------------- 2 files changed, 235 insertions(+), 173 deletions(-) diff --git a/src/translations/bitmessage_pl.qm b/src/translations/bitmessage_pl.qm index 16f9595f4a01b87a27c4a7b7cbc718f396afb85f..c02118e4fa3b0d749d8214ba77f336a17e84f342 100644 GIT binary patch delta 4842 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 NfTn2qDIF`3{vTBCQUd@0 delta 3400 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 B`fvaM diff --git a/src/translations/bitmessage_pl.ts b/src/translations/bitmessage_pl.ts index 0875084d..4b2b6371 100644 --- a/src/translations/bitmessage_pl.ts +++ b/src/translations/bitmessage_pl.ts @@ -58,12 +58,12 @@ EmailGatewayRegistrationDialog - + Registration failed: Rejestracja nie powiodła się: - + The requested email address is not available, please try a new one. Fill out the new desired email address (including @mailchuck.com) below: Wybrany adres e-mail nie jest dostępny, proszę spróbować inny. Wpisz adres poniżej (razem z końcówką @mailchuck.com): @@ -170,52 +170,52 @@ Wprowadź wybrany adres e-mail (razem z końcówką @mailchuck.com) poniżej: MainWindow - + Reply to sender Odpowiedz do nadawcy - + Reply to channel Odpowiedz do kanału - + Add sender to your Address Book Dodaj nadawcę do Książki Adresowej - + Add sender to your Blacklist Dodaj nadawcę do Listy Blokowanych - + Move to Trash Przenieś do kosza - + Undelete Przywróć - + View HTML code as formatted text Wyświetl kod HTML w postaci sformatowanej - + Save message as... Zapisz wiadomość jako... - + Mark Unread Oznacz jako nieprzeczytane - + New Nowe @@ -240,12 +240,12 @@ Wprowadź wybrany adres e-mail (razem z końcówką @mailchuck.com) poniżej:Kopiuj adres do schowka - + Special address behavior... Specjalne zachowanie adresu... - + Email gateway Przekaźnik e-mail @@ -255,37 +255,37 @@ Wprowadź wybrany adres e-mail (razem z końcówką @mailchuck.com) poniżej:Usuń - + Send message to this address Wyślij wiadomość pod ten adres - + Subscribe to this address Subskrybuj ten adres - + Add New Address Dodaj nowy adres - + Copy destination address to clipboard Kopiuj adres odbiorcy do schowka - + Force send Wymuś wysłanie - + One of your addresses, %1, is an old version 1 address. Version 1 addresses are no longer supported. May we delete it now? Jeden z adresów, %1, jest starym adresem wersji 1. Adresy tej wersji nie są już wspierane. Usunąć go? - + Waiting for their encryption key. Will request it again soon. Oczekiwanie na klucz szyfrujący odbiorcy. Niedługo nastąpi ponowne wysłanie o niego prośby. @@ -295,17 +295,17 @@ Wprowadź wybrany adres e-mail (razem z końcówką @mailchuck.com) poniżej: - + Queued. W kolejce do wysłania. - + Message sent. Waiting for acknowledgement. Sent at %1 Wiadomość wysłana. Oczekiwanie na potwierdzenie odbioru. Wysłano o %1 - + Message sent. Sent at %1 Wiadomość wysłana. Wysłano o %1 @@ -315,47 +315,47 @@ Wprowadź wybrany adres e-mail (razem z końcówką @mailchuck.com) poniżej: - + Acknowledgement of the message received %1 Otrzymano potwierdzenie odbioru wiadomości %1 - + Broadcast queued. Przekaz w kolejce do wysłania. - + Broadcast on %1 Wysłana o %1 - + Problem: The work demanded by the recipient is more difficult than you are willing to do. %1 Problem: dowód pracy wymagany przez odbiorcę jest trudniejszy niż zaakceptowany przez Ciebie. %1 - + Problem: The recipient's encryption key is no good. Could not encrypt message. %1 Problem: klucz szyfrujący odbiorcy jest nieprawidłowy. Nie można zaszyfrować wiadomości. %1 - + Forced difficulty override. Send should start soon. Wymuszono ominięcie trudności. Wysłanie zostanie wkrótce rozpoczęte. - + Unknown status: %1 %2 Nieznany status: %1 %2 - + Not Connected Brak połączenia - + Show Bitmessage Pokaż Bitmessage @@ -365,12 +365,12 @@ Wprowadź wybrany adres e-mail (razem z końcówką @mailchuck.com) poniżej:Wyślij - + Subscribe Subskrybuj - + Channel Kanał @@ -380,12 +380,12 @@ Wprowadź wybrany adres e-mail (razem z końcówką @mailchuck.com) poniżej:Zamknij - + You may manage your keys by editing the keys.dat file stored in the same directory as this program. It is important that you back up this file. Możesz zarządzać swoimi kluczami edytując plik keys.dat znajdujący się w tym samym katalogu co program. Zaleca się zrobienie kopii zapasowej tego pliku. - + You may manage your keys by editing the keys.dat file stored in %1 It is important that you back up this file. @@ -394,17 +394,17 @@ It is important that you back up this file. Zaleca się zrobienie kopii zapasowej tego pliku. - + Open keys.dat? Otworzyć plik keys.dat? - + You may manage your keys by editing the keys.dat file stored in the same directory as this program. It is important that you back up this file. Would you like to open the file now? (Be sure to close Bitmessage before making any changes.) Możesz zarządzać swoimi kluczami edytując plik keys.dat znajdujący się w tym samym katalogu co program. Zaleca się zrobienie kopii zapasowej tego pliku. Czy chcesz otworzyć ten plik teraz? (Zamknij Bitmessage, przed wprowadzeniem jakichkolwiek zmian.) - + You may manage your keys by editing the keys.dat file stored in %1 It is important that you back up this file. Would you like to open the file now? (Be sure to close Bitmessage before making any changes.) @@ -413,37 +413,37 @@ It is important that you back up this file. Would you like to open the file now? Zaleca się zrobienie kopii zapasowej tego pliku. Czy chcesz otworzyć ten plik teraz? (Zamknij Bitmessage przed wprowadzeniem jakichkolwiek zmian.) - + Delete trash? Opróżnić kosz? - + Are you sure you want to delete all trashed messages? Czy na pewno usunąć wszystkie wiadomości z kosza? - + bad passphrase nieprawidłowe hasło - + You must type your passphrase. If you don't have one then this is not the form for you. Musisz wpisać swoje hasło. Jeżeli go nie posiadasz, to ten formularz nie jest dla Ciebie. - + Bad address version number Nieprawidłowy numer wersji adresu - + Your address version number must be a number: either 3 or 4. Twój numer wersji adresu powinien wynosić: 3 lub 4. - + Your address version number must be either 3 or 4. Twój numer wersji adresu powinien wynosić: 3 lub 4. @@ -513,22 +513,22 @@ Zaleca się zrobienie kopii zapasowej tego pliku. Czy chcesz otworzyć ten plik - + Connection lost Połączenie utracone - + Connected Połączono - + Message trashed Wiadomość usunięta - + The TTL, or Time-To-Live is the length of time that the network will hold the message. The recipient must get it during this time. If your Bitmessage client does not hear an acknowledgement, it will resend the message automatically. The longer the Time-To-Live, the @@ -539,17 +539,17 @@ Im dłuższy TTL, tym więcej pracy będzie musiał wykonac komputer wysyłając Zwykle 4-5 dniowy TTL jest odpowiedni. - + Message too long Wiadomość zbyt długa - + The message that you are trying to send is too long by %1 bytes. (The maximum is 261644 bytes). Please cut it down before sending. Wiadomość jest za długa o %1 bajtów (maksymalna długość wynosi 261644 bajty). Przed wysłaniem należy ją skrócić. - + Error: Your account wasn't registered at an email gateway. Sending registration now as %1, please wait for the registration to be processed before retrying sending. Błąd: Twoje konto nie było zarejestrowane w bramce poczty. Rejestrowanie jako %1, proszę poczekać na zakończenie procesu przed ponowną próbą wysłania wiadomości. @@ -594,67 +594,67 @@ Zwykle 4-5 dniowy TTL jest odpowiedni. - + Error: You must specify a From address. If you don't have one, go to the 'Your Identities' tab. Błąd: musisz wybrać adres wysyłania. Jeżeli go nie posiadasz, przejdź do zakładki 'Twoje tożsamości'. - + Address version number Numer wersji adresu - + Concerning the address %1, Bitmessage cannot understand address version numbers of %2. Perhaps upgrade Bitmessage to the latest version. Odnośnie adresu %1, Bitmessage nie potrafi odczytać wersji adresu %2. Może uaktualnij Bitmessage do najnowszej wersji. - + Stream number Numer strumienia - + Concerning the address %1, Bitmessage cannot handle stream numbers of %2. Perhaps upgrade Bitmessage to the latest version. Odnośnie adresu %1, Bitmessage nie potrafi operować na strumieniu adresu %2. Może uaktualnij Bitmessage do najnowszej wersji. - + Warning: You are currently not connected. Bitmessage will do the work necessary to send the message but it won't send until you connect. Uwaga: nie jesteś obecnie połączony. Bitmessage wykona niezbędną pracę do wysłania wiadomości, ale nie wyśle jej póki się nie połączysz. - + Message queued. W kolejce do wysłania - + Your 'To' field is empty. Pole 'Do' jest puste - + Right click one or more entries in your address book and select 'Send message to this address'. Użyj prawego przycisku myszy na adresie z książki adresowej i wybierz opcję "Wyślij wiadomość do tego adresu". - + Fetched address from namecoin identity. Pobrano adres z identyfikatora Namecoin. - + New Message Nowa wiadomość - + From Od - + Sending email gateway registration request Wysyłanie zapytania o rejestrację na bramce poczty @@ -669,142 +669,142 @@ Zwykle 4-5 dniowy TTL jest odpowiedni. Wprowadzono niewłaściwy adres, który został zignorowany. - + Error: You cannot add the same address to your address book twice. Try renaming the existing one if you want. Błąd: Adres znajduje się już w książce adresowej. - + Error: You cannot add the same address to your subscriptions twice. Perhaps rename the existing one if you want. Błąd: Adres znajduje się już na liście subskrybcji. - + Restart Uruchom ponownie - + You must restart Bitmessage for the port number change to take effect. Musisz zrestartować Bitmessage, aby zmiana numeru portu weszła w życie. - + Bitmessage will use your proxy from now on but you may want to manually restart Bitmessage now to close existing connections (if any). Bitmessage będzie of teraz korzystał z serwera proxy, ale możesz ręcznie zrestartować Bitmessage, aby zamknąć obecne połączenia (jeżeli występują). - + Number needed Wymagany numer - + Your maximum download and upload rate must be numbers. Ignoring what you typed. Maksymalne prędkości wysyłania i pobierania powinny być liczbami. Zignorowano zmiany. - + Will not resend ever Nigdy nie wysyłaj ponownie - + Note that the time limit you entered is less than the amount of time Bitmessage waits for the first resend attempt therefore your messages will never be resent. Zauważ, że wpisany limit czasu wynosi mniej niż czas, który Bitmessage czeka przed pierwszą ponowną próbą wysłania wiadomości, więc Twoje wiadomości nie zostaną nigdy wysłane ponownie. - + Sending email gateway unregistration request Wysyłanie zapytania o wyrejestrowanie z bramki poczty - + Sending email gateway status request Wysyłanie zapytania o stan bramki poczty - + Passphrase mismatch Hasła różnią się - + The passphrase you entered twice doesn't match. Try again. Hasła, które wpisałeś nie pasują. Spróbuj ponownie. - + Choose a passphrase Wpisz hasło - + You really do need a passphrase. Naprawdę musisz wpisać hasło. - + Address is gone Adres zniknął - + Bitmessage cannot find your address %1. Perhaps you removed it? Bitmessage nie może odnaleźć Twojego adresu %1. Może go usunąłeś? - + Address disabled Adres nieaktywny - + Error: The address from which you are trying to send is disabled. You'll have to enable it on the 'Your Identities' tab before using it. Błąd: adres, z którego próbowałeś wysłać wiadomość jest nieaktywny. Włącz go w zakładce 'Twoje tożsamości' zanim go użyjesz. - + Entry added to the Address Book. Edit the label to your liking. Dodano wpis do książki adresowej. Można teraz zmienić jego nazwę. - + Entry added to the blacklist. Edit the label to your liking. Dodano wpis do listy blokowanych. Można teraz zmienić jego nazwę. - + Error: You cannot add the same address to your blacklist twice. Try renaming the existing one if you want. Błąd: Adres znajduje się już na liście blokowanych. - + Moved items to trash. Przeniesiono wiadomości do kosza. - + Undeleted item. Przywróć wiadomość. - + Save As... Zapisz jako... - + Write error. Błąd zapisu. - + No addresses selected. Nie wybrano adresu. - + If you delete the subscription, messages that you already received will become inaccessible. Maybe you can consider disabling the subscription instead. Disabled subscriptions will not receive new messages, but you can still view messages you already received. Are you sure you want to delete the subscription? @@ -813,7 +813,7 @@ Are you sure you want to delete the subscription? Czy na pewno chcesz usunąć tę subskrypcję? - + If you delete the channel, messages that you already received will become inaccessible. Maybe you can consider disabling the channel instead. Disabled channels will not receive new messages, but you can still view messages you already received. Are you sure you want to delete the channel? @@ -822,92 +822,92 @@ Are you sure you want to delete the channel? Czy na pewno chcesz usunąć ten kanał? - + Do you really want to remove this avatar? Czy na pewno chcesz usunąć ten awatar? - + You have already set an avatar for this address. Do you really want to overwrite it? Już ustawiłeś awatar dla tego adresu. Czy na pewno chcesz go nadpisać? - + Start-on-login not yet supported on your OS. Start po zalogowaniu jeszcze nie jest wspierany pod Twoim systemem. - + Minimize-to-tray not yet supported on your OS. Minimalizacja do zasobnika nie jest jeszcze wspierana pod Twoim systemem. - + Tray notifications not yet supported on your OS. Powiadomienia w zasobniku nie są jeszcze wspierane pod Twoim systemem. - + Testing... Testowanie... - + This is a chan address. You cannot use it as a pseudo-mailing list. To jest adres kanału. Nie możesz go użyć jako pseudo-listy-dyskusyjnej. - + The address should start with ''BM-'' Adres powinien zaczynać sie od "BM-" - + The address is not typed or copied correctly (the checksum failed). Adres nie został skopiowany lub przepisany poprawnie (błąd sumy kontrolnej). - + The version number of this address is higher than this software can support. Please upgrade Bitmessage. Numer wersji tego adresu jest wyższy niż ten program może obsłużyć. Proszę zaktualizować Bitmessage. - + The address contains invalid characters. Adres zawiera nieprawidłowe znaki. - + Some data encoded in the address is too short. Niektóre dane zakodowane w adresie są za krótkie. - + Some data encoded in the address is too long. Niektóre dane zakodowane w adresie są za długie. - + Some data encoded in the address is malformed. Niektóre dane zakodowane w adresie są uszkodzone. - + Enter an address above. Wprowadź adres powyżej. - + Address is an old type. We cannot display its past broadcasts. Adres starego typu - + There are no recent broadcasts from this address to display. Brak niedawnych wiadomości przekazów do wyświetlenia. - + You are using TCP port %1. (This can be changed in the settings). Btimessage używa portu TCP %1. (Można go zmienić w ustawieniach). @@ -1117,47 +1117,47 @@ Czy na pewno chcesz usunąć ten kanał? Dodaj nowy wpis - + Display the %1 recent broadcast(s) from this address. Pokaż %1 ostatnich wiadomości przekazów z tego adresu. - + New version of PyBitmessage is available: %1. Download it from https://github.com/Bitmessage/PyBitmessage/releases/latest Nowa wersja Bitmessage jest dostępna: %1. Pobierz ją z https://github.com/Bitmessage/PyBitmessage/releases/latest - + Waiting for PoW to finish... %1% Oczekiwanie na wykonanie dowodu pracy... %1% - + Shutting down Pybitmessage... %1% Zamykanie PyBitmessage... %1% - + Waiting for objects to be sent... %1% Oczekiwanie na wysłanie obiektów... %1% - + Saving settings... %1% Zapisywanie ustawień... %1% - + Shutting down core... %1% Zamykanie rdzenia programu... %1% - + Stopping notifications... %1% Zatrzymywanie powiadomień... %1% - + Shutdown imminent... %1% Zaraz zamknę... %1% @@ -1167,17 +1167,17 @@ Czy na pewno chcesz usunąć ten kanał? %n godzina%n godziny%n godzin%n godzin - + %n day(s) %n dzień%n dni%n dni%n dni - + Shutting down PyBitmessage... %1% Zamykanie PyBitmessage... %1% - + Sent Wysłane @@ -1222,86 +1222,86 @@ Czy na pewno chcesz usunąć ten kanał? Uwaga: Twój dysk lub partycja jest pełny. Bitmessage zamknie się. - + Error! Could not find sender address (your address) in the keys.dat file. Błąd! Nie można odnaleźć adresu nadawcy (Twojego adresu) w pliku keys.dat. - + Doing work necessary to send broadcast... Wykonywanie dowodu pracy niezbędnego do wysłania przekazu... - + Broadcast sent on %1 Przekaz wysłane o %1 - + Encryption key was requested earlier. Prośba o klucz szyfrujący została już wysłana. - + Sending a request for the recipient's encryption key. Wysyłanie zapytania o klucz szyfrujący odbiorcy. - + Looking up the receiver's public key Wyszukiwanie klucza publicznego odbiorcy - + Problem: Destination is a mobile device who requests that the destination be included in the message but this is disallowed in your settings. %1 Problem: adres docelowy jest urządzeniem przenośnym, które wymaga, aby adres docelowy był zawarty w wiadomości, ale jest to zabronione w Twoich ustawieniach. %1 - + Doing work necessary to send message. There is no required difficulty for version 2 addresses like this. Wykonywanie dowodu pracy niezbędnego do wysłania wiadomości. Nie ma wymaganej trudności dla adresów w wersji 2, takich jak ten adres. - + Doing work necessary to send message. Receiver's required difficulty: %1 and %2 Wykonywanie dowodu pracy niezbędnego do wysłania wiadomości. Odbiorca wymaga trudności: %1 i %2 - + Problem: The work demanded by the recipient (%1 and %2) is more difficult than you are willing to do. %3 Problem: dowód pracy wymagany przez odbiorcę (%1 i %2) jest trudniejszy niż chciałbyś wykonać. %3 - + Problem: You are trying to send a message to yourself or a chan but your encryption key could not be found in the keys.dat file. Could not encrypt message. %1 Problem: próbujesz wysłać wiadomość do siebie lub na kanał, ale Twój klucz szyfrujący nie został znaleziony w pliku keys.dat. Nie można zaszyfrować wiadomości. %1 - + Doing work necessary to send message. Wykonywanie pracy potrzebnej do wysłania wiadomości. - + Message sent. Waiting for acknowledgement. Sent on %1 Wiadomość wysłana. Oczekiwanie na potwierdzenie odbioru. Wysłano o %1 - + Doing work necessary to request encryption key. Wykonywanie pracy niezbędnej do prośby o klucz szyfrujący. - + Broadcasting the public key request. This program will auto-retry if they are offline. Rozsyłanie prośby o klucz publiczny. Program spróbuje ponownie, jeżeli jest on niepołączony. - + Sending public key request. Waiting for reply. Requested at %1 Wysyłanie prośby o klucz publiczny. Oczekiwanie na odpowiedź. Zapytano o %1 @@ -1311,142 +1311,196 @@ Odbiorca wymaga trudności: %1 i %2 Mapowanie portów UPnP wykonano na porcie %1 - + UPnP port mapping removed Usunięto mapowanie portów UPnP - + Mark all messages as read Oznacz wszystkie jako przeczytane - + Are you sure you would like to mark all messages read? Czy na pewno chcesz oznaczyć wszystkie wiadomości jako przeczytane? - + Doing work necessary to send broadcast. Wykonywanie dowodu pracy niezbędnego do wysłania przekazu. - + Proof of work pending Dowód pracy zawieszony - + %n object(s) pending proof of work Zawieszony dowód pracy %n obiektuZawieszony dowód pracy %n obiektówZawieszony dowód pracy %n obiektówZawieszony dowód pracy %n obiektów - + %n object(s) waiting to be distributed %n obiekt oczekuje na wysłanie%n obiektów oczekuje na wysłanie%n obiektów oczekuje na wysłanie%n obiektów oczekuje na wysłanie - + Wait until these tasks finish? Czy poczekać aż te zadania zostaną zakończone? - + Problem communicating with proxy: %1. Please check your network settings. Błąd podczas komunikacji z proxy: %1. Proszę sprawdź swoje ustawienia sieci. - + SOCKS5 Authentication problem: %1. Please check your SOCKS5 settings. Problem z autoryzacją SOCKS5: %1. Proszę sprawdź swoje ustawienia SOCKS5. - + The time on your computer, %1, may be wrong. Please verify your settings. Czas na Twoim komputerze, %1, może być błędny. Proszę sprawdź swoje ustawienia. + + + The name %1 was not found. + Ksywka %1 nie została znaleziona. + + + + The namecoin query failed (%1) + Zapytanie namecoin nie powiodło się (%1) + + + + The namecoin query failed. + Zapytanie namecoin nie powiodło się. + + + + The name %1 has no valid JSON data. + Ksywka %1 nie zawiera prawidłowych danych JSON. + + + + The name %1 has no associated Bitmessage address. + Ksywka %1 nie ma powiązanego adresu Bitmessage. + + + + Success! Namecoind version %1 running. + Namecoind wersja %1 działa poprawnie! + + + + Success! NMControll is up and running. + NMControl działa poprawnie! + + + + Couldn't understand NMControl. + Nie można zrozumieć NMControl. + Your GPU(s) did not calculate correctly, disabling OpenCL. Please report to the developers. Twoje procesory graficzne nie obliczyły poprawnie, wyłączam OpenCL. Prosimy zaraportować przypadek twórcom programu. - + + + Welcome to easy and secure Bitmessage + * send messages to other people + * send broadcast messages like twitter or + * discuss in chan(nel)s with other people + + +Witamy w przyjaznym i bezpiecznym Bitmessage +* wysyłaj wiadomości do innych użytkowników +* wysyłaj wiadomości subskrypcji (jak na Twitterze) +* dyskutuj na kanałach (chany) z innymi ludźmi + + + not recommended for chans niezalecany dla kanałów - + Problems connecting? Try enabling UPnP in the Network Settings Problem z połączeniem? Spróbuj włączyć UPnP w ustawieniach sieci. - + Error: Bitmessage addresses start with BM- Please check the recipient address %1 Błąd: adresy Bitmessage zaczynają się od BM-. Proszę sprawdzić adres odbiorcy %1. - + Error: The recipient address %1 is not typed or copied correctly. Please check it. Błąd: adres odbiorcy %1 nie został skopiowany lub przepisany poprawnie. Proszę go sprawdzić. - + Error: The recipient address %1 contains invalid characters. Please check it. Błąd: adres odbiorcy %1 zawiera nieprawidłowe znaki. Proszę go sprawdzić. - + Error: The version of the recipient address %1 is too high. Either you need to upgrade your Bitmessage software or your acquaintance is being clever. Błąd: wersja adresu odbiorcy %1 jest za wysoka. Musisz albo zaktualizować Twoje oprogramowanie Bitmessage, albo twój znajomy Cię trolluje. - + Error: Some data encoded in the recipient address %1 is too short. There might be something wrong with the software of your acquaintance. Błąd: niektóre dane zakodowane w adresie odbiorcy %1 są zbyt krótkie. Być może coś nie działa należycie w programie Twojego znajomego. - + Error: Some data encoded in the recipient address %1 is too long. There might be something wrong with the software of your acquaintance. Błąd: niektóre dane zakodowane w adresie odbiorcy %1 są zbyt długie. Być może coś nie działa należycie w programie Twojego znajomego. - + Error: Some data encoded in the recipient address %1 is malformed. There might be something wrong with the software of your acquaintance. Błąd: niektóre dane zakodowane w adresie odbiorcy %1 są uszkodzone. Być może coś nie działa należycie w programie Twojego znajomego. - + Error: Something is wrong with the recipient address %1. Błąd: coś jest nie tak z adresem odbiorcy %1. - + Synchronisation pending Synchronizacja zawieszona - + Bitmessage hasn't synchronised with the network, %n object(s) to be downloaded. If you quit now, it may cause delivery delays. Wait until the synchronisation finishes? Bitmessage nie zsynchronizował się z siecią, %n obiekt oczekuje na pobranie. Jeżeli zamkniesz go teraz, może to spowodować opóźnienia dostarczeń. Czy poczekać na zakończenie synchronizacji?Bitmessage nie zsynchronizował się z siecią, %n obiekty oczekują na pobranie. Jeżeli zamkniesz go teraz, może to spowodować opóźnienia dostarczeń. Czy poczekać na zakończenie synchronizacji?Bitmessage nie zsynchronizował się z siecią, %n obiektów oczekuje na pobranie. Jeżeli zamkniesz go teraz, może to spowodować opóźnienia dostarczeń. Czy poczekać na zakończenie synchronizacji?Bitmessage nie zsynchronizował się z siecią, %n obiektów oczekuje na pobranie. Jeżeli zamkniesz go teraz, może to spowodować opóźnienia dostarczeń. Czy poczekać na zakończenie synchronizacji? - + Not connected Niepołączony - + Bitmessage isn't connected to the network. If you quit now, it may cause delivery delays. Wait until connected and the synchronisation finishes? Bitmessage nie połączył się z siecią. Jeżeli zamkniesz go teraz, może to spowodować opóźnienia dostarczeń. Czy poczekać na połączenie i zakończenie synchronizacji? - + Waiting for network connection... Oczekiwanie na połączenie sieciowe... - + Waiting for finishing synchronisation... Oczekiwanie na zakończenie synchronizacji... @@ -1683,7 +1737,7 @@ Generowanie adresów 'losowych' jest wybrane domyślnie, jednak determ Use a Blacklist (Allow all incoming messages except those on the Blacklist) - Użyj czarnej listy (zezwala na wszystkie przychodzące z wyjątkiem od tych na czarnej liście) + Użyj czarnej listy (zezwala na wszystkie przychodzące wiadomości, z wyjątkiem tych na czarnej liście) @@ -2007,6 +2061,14 @@ Generowanie adresów 'losowych' jest wybrane domyślnie, jednak determ Moduł C PoW niedostępny. Prosimy zbudować go. + + qrcodeDialog + + + QR-code + Kod QR + + regenerateAddressesDialog @@ -2115,7 +2177,7 @@ Generowanie adresów 'losowych' jest wybrane domyślnie, jednak determ Use Identicons - Użyj 'Identiconów' + Użyj graficznych awatarów @@ -2331,7 +2393,7 @@ Generowanie adresów 'losowych' jest wybrane domyślnie, jednak determ Give up after - Nie wysyłaj ponownie po + Poddaj się po @@ -2361,7 +2423,7 @@ Generowanie adresów 'losowych' jest wybrane domyślnie, jednak determ Maximum outbound connections: [0: none] - Maksymalnych połączeń wychodzących: [0: none] + Maksymalnych połączeń wychodzących: [0: brak] From 76ae62c1f0fa47483f0fe8d3e4162eaa938a376a Mon Sep 17 00:00:00 2001 From: PyBitmessage Translations Date: Mon, 6 Mar 2017 16:33:54 +0100 Subject: [PATCH 009/407] Auto-updated language ja from transifex --- src/translations/bitmessage_ja.qm | Bin 63931 -> 65387 bytes src/translations/bitmessage_ja.ts | 347 +++++++++++++++++------------- 2 files changed, 201 insertions(+), 146 deletions(-) diff --git a/src/translations/bitmessage_ja.qm b/src/translations/bitmessage_ja.qm index 2f28764b49a278962b8b6ec52695070ec412fd6c..8bb153f722bb4e14ae205fea85013acba0c656ef 100644 GIT binary patch delta 4539 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 EmailGatewayRegistrationDialog - + Registration failed: 登録に失敗しました: - + The requested email address is not available, please try a new one. Fill out the new desired email address (including @mailchuck.com) below: リクエストしたメールアドレスは利用できません。新しいメールアドレスをお試しください。 新しい希望メールアドレス (@mailchuck.com を含む) を次のように記入してください: @@ -283,7 +283,7 @@ Please type the desired email address (including @mailchuck.com) below: %1は古いバージョン1のアドレスです。バージョン1のアドレスはサポートが終了しています。すぐに削除しますか? - + Waiting for their encryption key. Will request it again soon. 暗号鍵を待っています。 すぐにもう一度リクエストします。 @@ -293,17 +293,17 @@ Please type the desired email address (including @mailchuck.com) below: - + Queued. キューに入りました。 - + Message sent. Waiting for acknowledgement. Sent at %1 メッセージを送信しました。 確認応答を待っています。 %1 で送信されました - + Message sent. Sent at %1 メッセージは送信されました。送信先: %1 @@ -313,47 +313,47 @@ Please type the desired email address (including @mailchuck.com) below: - + Acknowledgement of the message received %1 メッセージの確認を受け取りました %1 - + Broadcast queued. 配信がキューに入りました。 - + Broadcast on %1 配信: %1 - + Problem: The work demanded by the recipient is more difficult than you are willing to do. %1 問題: 受信者が要求している処理は現在あなたが設定しているよりも高い難易度です。 %1 - + Problem: The recipient's encryption key is no good. Could not encrypt message. %1 問題: 受信者の暗号鍵は正当でない物です。メッセージを暗号化できません。 %1 - + Forced difficulty override. Send should start soon. 難易度を強制上書きしました。まもなく送信されます。 - + Unknown status: %1 %2 不明なステータス: %1 %2 - + Not Connected 未接続 - + Show Bitmessage Bitmessageを表示 @@ -363,12 +363,12 @@ Please type the desired email address (including @mailchuck.com) below: 送る - + Subscribe 購読 - + Channel チャンネル @@ -378,66 +378,66 @@ Please type the desired email address (including @mailchuck.com) below: 終了 - + You may manage your keys by editing the keys.dat file stored in the same directory as this program. It is important that you back up this file. プログラムを同じディレクトリに保存されているkeys.datファイルを編集することで鍵を管理できます。ファイルをバックアップしておくことも重要です。 - + You may manage your keys by editing the keys.dat file stored in %1 It is important that you back up this file. %1に保存されているkeys.datファイルを編集することで鍵を管理できます。ファイルをバックアップしておくことも重要です。 - + Open keys.dat? keys.datを開きますか? - + You may manage your keys by editing the keys.dat file stored in the same directory as this program. It is important that you back up this file. Would you like to open the file now? (Be sure to close Bitmessage before making any changes.) プログラムを同じディレクトリに保存されているkeys.datファイルを編集することで鍵を管理できます。ファイルをバックアップしておくことも重要です。すぐにファイルを開きますか?(必ず編集する前にBitmessageを終了してください) - + You may manage your keys by editing the keys.dat file stored in %1 It is important that you back up this file. Would you like to open the file now? (Be sure to close Bitmessage before making any changes.) %1に保存されているkeys.datファイルを編集することで鍵を管理できます。ファイルをバックアップしておくことも重要です。すぐにファイルを開きますか?(必ず編集する前にBitmessageを終了してください) - + Delete trash? ゴミ箱を空にしますか? - + Are you sure you want to delete all trashed messages? ゴミ箱内のメッセージを全て削除してもよろしいですか? - + bad passphrase 不正なパスフレーズ - + You must type your passphrase. If you don't have one then this is not the form for you. パスフレーズを入力してください。パスフレーズがない場合は入力する必要はありません。 - + Bad address version number 不正なアドレスのバージョン番号 - + Your address version number must be a number: either 3 or 4. アドレスのバージョン番号は数字にする必要があります: 3 または 4。 - + Your address version number must be either 3 or 4. アドレスのバージョン番号は、3 または 4 のどちらかにする必要があります。 @@ -507,22 +507,22 @@ It is important that you back up this file. Would you like to open the file now? - + Connection lost 接続が切断されました - + Connected 接続済み - + Message trashed メッセージが削除されました - + The TTL, or Time-To-Live is the length of time that the network will hold the message. The recipient must get it during this time. If your Bitmessage client does not hear an acknowledgement, it will resend the message automatically. The longer the Time-To-Live, the @@ -533,17 +533,17 @@ It is important that you back up this file. Would you like to open the file now? コンピュータがメッセージを送信するために必要な処理が増えます。 多くの場合 4〜5 日のTTL(Time-To-Live)が適切です。 - + Message too long メッセージが長すぎます - + The message that you are trying to send is too long by %1 bytes. (The maximum is 261644 bytes). Please cut it down before sending. 送信しようとしているメッセージが %1 バイト長すぎます。 (最大は261644バイトです)。 送信する前に短くしてください。 - + Error: Your account wasn't registered at an email gateway. Sending registration now as %1, please wait for the registration to be processed before retrying sending. エラー: アカウントがメールゲートウェイに登録されていません。 今 %1 として登録を送信しています。送信を再試行する前に、登録が処理されるまでお待ちください。 @@ -588,67 +588,67 @@ It is important that you back up this file. Would you like to open the file now? - + Error: You must specify a From address. If you don't have one, go to the 'Your Identities' tab. エラー: 送信元アドレスを指定してください。まだ作成していない場合には「アドレス一覧」のタブを開いてください。 - + Address version number アドレスのバージョン番号 - + Concerning the address %1, Bitmessage cannot understand address version numbers of %2. Perhaps upgrade Bitmessage to the latest version. アドレス %1 に接続。%2 のバージョン番号は処理できません。Bitmessageを最新のバージョンへアップデートしてください。 - + Stream number ストリーム番号 - + Concerning the address %1, Bitmessage cannot handle stream numbers of %2. Perhaps upgrade Bitmessage to the latest version. アドレス %1 に接続。%2 のストリーム番号は処理できません。Bitmessageを最新のバージョンへアップデートしてください。 - + Warning: You are currently not connected. Bitmessage will do the work necessary to send the message but it won't send until you connect. 警告: 接続されていません。Bitmessageはメッセージの処理を行いますが、ネットワークに接続するまで送信はされません。 - + Message queued. メッセージがキューに入りました。 - + Your 'To' field is empty. 宛先が指定されていません。 - + Right click one or more entries in your address book and select 'Send message to this address'. アドレス帳から一つ、または複数のアドレスを右クリックして「このアドレスへ送信」を選んでください。 - + Fetched address from namecoin identity. namecoin IDからアドレスを取得。 - + New Message 新規メッセージ - + From 送信元 - + Sending email gateway registration request メールゲートウェイの登録リクエストを送信しています @@ -663,142 +663,142 @@ It is important that you back up this file. Would you like to open the file now? 入力されたアドレスは不正です。無視されました。 - + Error: You cannot add the same address to your address book twice. Try renaming the existing one if you want. エラー: 同じアドレスを複数アドレス帳に追加する事はできません。既存の項目をリネームしてください。 - + Error: You cannot add the same address to your subscriptions twice. Perhaps rename the existing one if you want. エラー: 購読に、同じアドレスを2回追加することはできません。 必要に応じて、既存の名前を変更してください。 - + Restart 再開 - + You must restart Bitmessage for the port number change to take effect. ポート番号の変更を有効にするにはBitmessageを再起動してください。 - + Bitmessage will use your proxy from now on but you may want to manually restart Bitmessage now to close existing connections (if any). プロキシの設定を有効にするには手動でBitmessageを再起動してください。既に接続がある場合は切断されます。 - + Number needed 数字が必要です - + Your maximum download and upload rate must be numbers. Ignoring what you typed. 最大ダウンロード数とアップロード数は数字にする必要があります。 入力されたものを無視します。 - + Will not resend ever 今後再送信されません - + Note that the time limit you entered is less than the amount of time Bitmessage waits for the first resend attempt therefore your messages will never be resent. 入力した時間制限は、Bitmessageが最初の再送試行を待つ時間よりも短いため、メッセージは再送信されないことにご注意ください。 - + Sending email gateway unregistration request メールゲートウェイの登録抹消リクエストを送信しています - + Sending email gateway status request メールゲートウェイの状態リクエストを送信しています - + Passphrase mismatch パスフレーズが一致しません - + The passphrase you entered twice doesn't match. Try again. 再度入力されたパスフレーズが一致しません。再入力してください。 - + Choose a passphrase パスフレーズを選択してください - + You really do need a passphrase. パスフレーズが必要です。 - + Address is gone アドレスが無効になりました - + Bitmessage cannot find your address %1. Perhaps you removed it? アドレス %1 が見つかりません。既に削除していませんか? - + Address disabled アドレスが無効になりました - + Error: The address from which you are trying to send is disabled. You'll have to enable it on the 'Your Identities' tab before using it. エラー: 送信しようとしたアドレスは無効になっています。使用する前に「アドレス一覧」で有効にしてください。 - + Entry added to the Address Book. Edit the label to your liking. アドレス帳に項目が追加されました。ラベルは自由に編集できます。 - + Entry added to the blacklist. Edit the label to your liking. ブラックリストに項目が追加されました。ラベルは自由に編集できます。 - + Error: You cannot add the same address to your blacklist twice. Try renaming the existing one if you want. エラー: ブラックリストに同じアドレスを2回追加することはできません。 必要に応じて既存の名前を変更してみてください。 - + Moved items to trash. アイテムをゴミ箱へ移動。 - + Undeleted item. アイテムの削除を元に戻します。 - + Save As... 形式を選択して保存 - + Write error. 書き込みエラー。 - + No addresses selected. アドレスが未選択です。 - + If you delete the subscription, messages that you already received will become inaccessible. Maybe you can consider disabling the subscription instead. Disabled subscriptions will not receive new messages, but you can still view messages you already received. Are you sure you want to delete the subscription? @@ -807,7 +807,7 @@ Are you sure you want to delete the subscription? 購読を削除してもよろしいですか? - + If you delete the channel, messages that you already received will become inaccessible. Maybe you can consider disabling the channel instead. Disabled channels will not receive new messages, but you can still view messages you already received. Are you sure you want to delete the channel? @@ -816,92 +816,92 @@ Are you sure you want to delete the channel? チャンネルを削除してもよろしいですか? - + Do you really want to remove this avatar? このアバターを削除してもよろしいですか? - + You have already set an avatar for this address. Do you really want to overwrite it? すでにこのアドレスのアバターを設定しています。 上書きしてもよろしいですか? - + Start-on-login not yet supported on your OS. ログイン時に開始は、まだお使いのOSでサポートされていません。 - + Minimize-to-tray not yet supported on your OS. トレイに最小化は、まだお使いのOSでサポートされていません。 - + Tray notifications not yet supported on your OS. トレイ通知は、まだお使いのOSでサポートされていません。 - + Testing... テスト中 - + This is a chan address. You cannot use it as a pseudo-mailing list. chanアドレスは仮想メーリングリストのアドレスには使用できません。 - + The address should start with ''BM-'' アドレスは「BM-」から始まります - + The address is not typed or copied correctly (the checksum failed). このアドレスは正しく入力、またはコピーされていません。(チェックサムが一致しません)。 - + The version number of this address is higher than this software can support. Please upgrade Bitmessage. このアドレスのバージョン番号はこのプログラムのサポート範囲外です。Bitmessageをアップデートしてください。 - + The address contains invalid characters. 入力されたアドレスは不正な文字を含んでいます。 - + Some data encoded in the address is too short. このアドレスでエンコードされたデータが短すぎます。 - + Some data encoded in the address is too long. このアドレスでエンコードされたデータが長過ぎます。 - + Some data encoded in the address is malformed. このアドレスでエンコードされた一部のデータが不正です。 - + Enter an address above. 上にアドレスを入力してください。 - + Address is an old type. We cannot display its past broadcasts. アドレスが古い形式です。 過去の配信は表示できません。 - + There are no recent broadcasts from this address to display. このアドレスから表示する最近の配信はありません。 - + You are using TCP port %1. (This can be changed in the settings). 使用中のポート %1 (設定で変更できます)。 @@ -1111,47 +1111,47 @@ Are you sure you want to delete the channel? 新しい項目を追加 - + Display the %1 recent broadcast(s) from this address. このアドレスから%1の最新の配信を表示します。 - + New version of PyBitmessage is available: %1. Download it from https://github.com/Bitmessage/PyBitmessage/releases/latest 新しいバージョンの PyBitmessage が利用可能です: %1。 https://github.com/Bitmessage/PyBitmessage/releases/latest からダウンロードしてください - + Waiting for PoW to finish... %1% PoW(証明)が完了するのを待っています... %1% - + Shutting down Pybitmessage... %1% Pybitmessageをシャットダウンしています... %1% - + Waiting for objects to be sent... %1% オブジェクトの送信待ち... %1% - + Saving settings... %1% 設定を保存しています... %1% - + Shutting down core... %1% コアをシャットダウンしています... %1% - + Stopping notifications... %1% 通知を停止しています... %1% - + Shutdown imminent... %1% すぐにシャットダウンします... %1% @@ -1161,17 +1161,17 @@ Are you sure you want to delete the channel? %n 時間 - + %n day(s) %n 日 - + Shutting down PyBitmessage... %1% PyBitmessageをシャットダウンしています... %1% - + Sent 送信済 @@ -1216,86 +1216,86 @@ Are you sure you want to delete the channel? アラート: ディスクまたはデータストレージのボリュームがいっぱいです。 Bitmessageが終了します。 - + Error! Could not find sender address (your address) in the keys.dat file. エラー! keys.datファイルで送信元アドレス (あなたのアドレス) を見つけることができませんでした。 - + Doing work necessary to send broadcast... 配信に必要な処理を行っています... - + Broadcast sent on %1 配信が送信されました %1 - + Encryption key was requested earlier. 暗号鍵は以前にリクエストされました。 - + Sending a request for the recipient's encryption key. 受信者の暗号鍵のリクエストを送信します。 - + Looking up the receiver's public key 受信者の公開鍵を探しています - + Problem: Destination is a mobile device who requests that the destination be included in the message but this is disallowed in your settings. %1 問題: メッセージに含まれた宛先のリクエストはモバイルデバイスですが、設定では許可されていません。 %1 - + Doing work necessary to send message. There is no required difficulty for version 2 addresses like this. メッセージの送信に必要な処理を行っています。 このようなバージョン2のアドレスには、必要な難易度はありません。 - + Doing work necessary to send message. Receiver's required difficulty: %1 and %2 メッセージの送信に必要な処理を行っています。 受信者の必要な難易度: %1 および %2 - + Problem: The work demanded by the recipient (%1 and %2) is more difficult than you are willing to do. %3 問題: 受信者が要求している処理 (%1 および %2) は、現在あなたが設定しているよりも高い難易度です。 %3 - + Problem: You are trying to send a message to yourself or a chan but your encryption key could not be found in the keys.dat file. Could not encrypt message. %1 問題: あなた自身またはチャンネルにメッセージを送信しようとしていますが、暗号鍵がkeys.datファイルに見つかりませんでした。 メッセージを暗号化できませんでした。 %1 - + Doing work necessary to send message. メッセージの送信に必要な処理を行っています。 - + Message sent. Waiting for acknowledgement. Sent on %1 メッセージを送信しました。 確認応答を待っています。 %1 で送信しました - + Doing work necessary to request encryption key. 暗号鍵のリクエストに必要な処理を行っています。 - + Broadcasting the public key request. This program will auto-retry if they are offline. 公開鍵のリクエストを配信しています。 このプログラムがオフラインの場合、自動的に再試行されます。 - + Sending public key request. Waiting for reply. Requested at %1 公開鍵のリクエストを送信しています。 返信を待っています。 %1 でリクエストしました @@ -1315,32 +1315,32 @@ Receiver's required difficulty: %1 and %2 すべてのメッセージを既読にする - + Are you sure you would like to mark all messages read? すべてのメッセージを既読にしてもよろしいですか? - + Doing work necessary to send broadcast. 配信に必要な処理を行っています。 - + Proof of work pending PoW(証明)を待っています - + %n object(s) pending proof of work %n オブジェクトが証明待ち (PoW) - + %n object(s) waiting to be distributed %n オブジェクトが配布待ち - + Wait until these tasks finish? これらのタスクが完了するまで待ちますか? @@ -1359,88 +1359,143 @@ Receiver's required difficulty: %1 and %2 The time on your computer, %1, may be wrong. Please verify your settings. お使いのコンピュータの時間 %1 は間違っている可能性があります。 設定を確認してください。 + + + The name %1 was not found. + 名前 %1 が見つかりませんでした。 + + + + The namecoin query failed (%1) + namecoin のクエリに失敗しました (%1) + + + + The namecoin query failed. + namecoin のクエリに失敗しました。 + + + + The name %1 has no valid JSON data. + 名前 %1 は有効な JSON データがありません。 + + + + The name %1 has no associated Bitmessage address. + 名前 %1 は関連付けられた Bitmessage アドレスがありません。 + + + + Success! Namecoind version %1 running. + 成功! Namecoind バージョン %1 が実行中。 + + + + Success! NMControll is up and running. + 成功! NMControll が開始して実行中です。 + + + + Couldn't understand NMControl. + NMControl を理解できませんでした。 + Your GPU(s) did not calculate correctly, disabling OpenCL. Please report to the developers. GPUが正しく求められないため、OpenCLが無効になりました。 開発者に報告してください。 - + + + Welcome to easy and secure Bitmessage + * send messages to other people + * send broadcast messages like twitter or + * discuss in chan(nel)s with other people + + +簡単で安全な Bitmessage へようこそ +* 他の人にメッセージを送ります +* Twitter のようなブロードキャストメッセージを送信します +* 他の人と一緒にチャン(ネル)で議論します + + + + not recommended for chans チャンネルにはお勧めしません - + Problems connecting? Try enabling UPnP in the Network Settings 接続に問題がありますか? ネットワーク設定でUPnPを有効にしてみてください - + Error: Bitmessage addresses start with BM- Please check the recipient address %1 エラー: BitmessageのアドレスはBM-で始まります。 受信者のアドレス %1 を確認してください - + Error: The recipient address %1 is not typed or copied correctly. Please check it. エラー: 受信者のアドレス %1 は正しく入力、またはコピーされていません。確認して下さい。 - + Error: The recipient address %1 contains invalid characters. Please check it. エラー: 受信者のアドレス %1 は不正な文字を含んでいます。確認して下さい。 - + Error: The version of the recipient address %1 is too high. Either you need to upgrade your Bitmessage software or your acquaintance is being clever. エラー: 受信者アドレスのバージョン %1 は高すぎます。 Bitmessageソフトウェアをアップグレードする必要があるか、連絡先が賢明になっているかのいずれかです。 - + Error: Some data encoded in the recipient address %1 is too short. There might be something wrong with the software of your acquaintance. エラー: アドレス %1 でエンコードされたデータが短すぎます。連絡先のソフトウェアが何かしら誤っている可能性があります。 - + Error: Some data encoded in the recipient address %1 is too long. There might be something wrong with the software of your acquaintance. エラー: 受信者のアドレス %1 でエンコードされたデータが短すぎます。連絡先のソフトウェアが何かしら誤っている可能性があります。 - + Error: Some data encoded in the recipient address %1 is malformed. There might be something wrong with the software of your acquaintance. エラー: 受信者のアドレス %1 でエンコードされたデータの一部が不正です。連絡先のソフトウェアが何かしら誤っている可能性があります。 - + Error: Something is wrong with the recipient address %1. エラー: 受信者のアドレス %1 には何かしら誤りがあります。 - + Synchronisation pending 同期を保留しています - + Bitmessage hasn't synchronised with the network, %n object(s) to be downloaded. If you quit now, it may cause delivery delays. Wait until the synchronisation finishes? Bitmessageはネットワークと同期していません。%n のオブジェクトをダウンロードする必要があります。 今、終了すると、配送が遅れることがあります。 同期が完了するまで待ちますか? - + Not connected 未接続 - + Bitmessage isn't connected to the network. If you quit now, it may cause delivery delays. Wait until connected and the synchronisation finishes? Bitmessageはネットワークに接続していません。 今、終了すると、配送が遅れることがあります。 接続して、同期が完了するまで待ちますか? - + Waiting for network connection... ネットワーク接続を待っています... - + Waiting for finishing synchronisation... 同期の完了を待っています... From fd871fca8589fbee920fee8fa6ccf5051efd047f Mon Sep 17 00:00:00 2001 From: PyBitmessage Translations Date: Mon, 6 Mar 2017 11:26:48 +0100 Subject: [PATCH 010/407] Auto-updated language ru from transifex --- src/translations/bitmessage_ru.qm | Bin 90153 -> 91853 bytes src/translations/bitmessage_ru.ts | 324 +++++++++++++++++------------- 2 files changed, 189 insertions(+), 135 deletions(-) diff --git a/src/translations/bitmessage_ru.qm b/src/translations/bitmessage_ru.qm index 9b7a09a9afb9c2cca2b14ccdf26cd57d8714c0cc..619e21fdf1089434e73f7438017fcfb0f55541d5 100644 GIT binary patch delta 4826 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 P%EL>-#PaC}qxSp{#TEF9 diff --git a/src/translations/bitmessage_ru.ts b/src/translations/bitmessage_ru.ts index c5cbbc69..a7115a1c 100644 --- a/src/translations/bitmessage_ru.ts +++ b/src/translations/bitmessage_ru.ts @@ -58,12 +58,12 @@ EmailGatewayRegistrationDialog - + Registration failed: Регистрация не удалась: - + The requested email address is not available, please try a new one. Fill out the new desired email address (including @mailchuck.com) below: Запрашиваемый адрес email недоступен, попробуйте ввести другой. Введите желаемый адрес (включая @mailchuck.com) ниже: @@ -283,7 +283,7 @@ Please type the desired email address (including @mailchuck.com) below: Один из Ваших адресов, %1, является устаревшим адресом версии 1. Адреса версии 1 больше не поддерживаются. Хотите ли Вы удалить его сейчас? - + Waiting for their encryption key. Will request it again soon. Ожидаем ключ шифрования от Вашего собеседника. Запрос будет повторен через некоторое время. @@ -293,17 +293,17 @@ Please type the desired email address (including @mailchuck.com) below: - + Queued. В очереди. - + Message sent. Waiting for acknowledgement. Sent at %1 Сообщение отправлено. Ожидаем подтверждения. Отправлено в %1 - + Message sent. Sent at %1 Сообщение отправлено в %1 @@ -313,47 +313,47 @@ Please type the desired email address (including @mailchuck.com) below: - + Acknowledgement of the message received %1 Сообщение доставлено в %1 - + Broadcast queued. Рассылка ожидает очереди. - + Broadcast on %1 Рассылка на %1 - + Problem: The work demanded by the recipient is more difficult than you are willing to do. %1 Проблема: Ваш получатель требует более сложных вычислений, чем максимум, указанный в Ваших настройках. %1 - + Problem: The recipient's encryption key is no good. Could not encrypt message. %1 Проблема: ключ получателя неправильный. Невозможно зашифровать сообщение. %1 - + Forced difficulty override. Send should start soon. Форсирована смена сложности. Отправляем через некоторое время. - + Unknown status: %1 %2 Неизвестный статус: %1 %2 - + Not Connected Не соединено - + Show Bitmessage Показать Bitmessage @@ -363,12 +363,12 @@ Please type the desired email address (including @mailchuck.com) below: Отправить - + Subscribe Подписки - + Channel Канал @@ -378,13 +378,13 @@ Please type the desired email address (including @mailchuck.com) below: Выйти - + You may manage your keys by editing the keys.dat file stored in the same directory as this program. It is important that you back up this file. Вы можете управлять Вашими ключами, отредактировав файл keys.dat, находящийся в той же папке, что и эта программа. Создайте резервную копию этого файла перед тем как будете его редактировать. - + You may manage your keys by editing the keys.dat file stored in %1 It is important that you back up this file. @@ -393,19 +393,19 @@ It is important that you back up this file. Создайте резервную копию этого файла перед тем как будете его редактировать. - + Open keys.dat? Открыть файл keys.dat? - + You may manage your keys by editing the keys.dat file stored in the same directory as this program. It is important that you back up this file. Would you like to open the file now? (Be sure to close Bitmessage before making any changes.) Вы можете управлять Вашими ключами, отредактировав файл keys.dat, находящийся в той же папке, что и эта программа. Создайте резервную копию этого файла перед тем как будете его редактировать. Хотели бы Вы открыть этот файл сейчас? (пожалуйста, закройте Bitmessage до того как Вы внесёте в этот файл какие-либо изменения.) - + You may manage your keys by editing the keys.dat file stored in %1 It is important that you back up this file. Would you like to open the file now? (Be sure to close Bitmessage before making any changes.) @@ -415,37 +415,37 @@ It is important that you back up this file. Would you like to open the file now? (пожалуйста, закройте Bitmessage до того как Вы внесёте в этот файл какие-либо изменения.) - + Delete trash? Очистить корзину? - + Are you sure you want to delete all trashed messages? Вы уверены что хотите очистить корзину? - + bad passphrase Неподходящая секретная фраза - + You must type your passphrase. If you don't have one then this is not the form for you. Вы должны ввести секретную фразу. Если Вы не хотите этого делать, то Вы выбрали неправильную опцию. - + Bad address version number - Неверный адрес номера версии + Неверный номер версии адреса - + Your address version number must be a number: either 3 or 4. Адрес номера версии должен быть числом: либо 3, либо 4. - + Your address version number must be either 3 or 4. Адрес номера версии должен быть либо 3, либо 4. @@ -515,22 +515,22 @@ It is important that you back up this file. Would you like to open the file now? - + Connection lost Соединение потеряно - + Connected Соединено - + Message trashed Сообщение удалено - + The TTL, or Time-To-Live is the length of time that the network will hold the message. The recipient must get it during this time. If your Bitmessage client does not hear an acknowledgement, it will resend the message automatically. The longer the Time-To-Live, the @@ -541,17 +541,17 @@ It is important that you back up this file. Would you like to open the file now? сообщение. Часто разумным вариантом будет установка TTL на 4 или 5 дней. - + Message too long Сообщение слишком длинное - + The message that you are trying to send is too long by %1 bytes. (The maximum is 261644 bytes). Please cut it down before sending. Сообщение, которое вы пытаетесь отправить, длиннее максимально допустимого на %1 байт. (Максимально допустимое значение 261644 байта). Пожалуйста, сократите сообщение перед отправкой. - + Error: Your account wasn't registered at an email gateway. Sending registration now as %1, please wait for the registration to be processed before retrying sending. Ошибка: ваш аккаунт не зарегистрирован на Email-шлюзе. Отправка регистрации %1, пожалуйста, подождите пока процесс регистрации не завершится, прежде чем попытаться отправить сообщение заново. @@ -596,67 +596,67 @@ It is important that you back up this file. Would you like to open the file now? - + Error: You must specify a From address. If you don't have one, go to the 'Your Identities' tab. - Вы должны указать адрес в поле "От кого". Вы можете найти Ваш адрес во вкладе "Ваши Адреса". + Вы должны указать адрес в поле "От кого". Вы можете найти Ваш адрес во вкладке "Ваши Адреса". - + Address version number Версия адреса - + Concerning the address %1, Bitmessage cannot understand address version numbers of %2. Perhaps upgrade Bitmessage to the latest version. По поводу адреса %1: Bitmessage не поддерживаем адреса версии %2. Возможно, Вам нужно обновить клиент Bitmessage. - + Stream number Номер потока - + Concerning the address %1, Bitmessage cannot handle stream numbers of %2. Perhaps upgrade Bitmessage to the latest version. По поводу адреса %1: Bitmessage не поддерживаем стрим номер %2. Возможно, Вам нужно обновить клиент Bitmessage. - + Warning: You are currently not connected. Bitmessage will do the work necessary to send the message but it won't send until you connect. Внимание: Вы не подключены к сети. Bitmessage выполнит работу, требуемую для отправки сообщения, но не отправит его до тех пор, пока Вы не подключитесь. - + Message queued. Сообщение в очереди. - + Your 'To' field is empty. Вы не заполнили поле 'Кому'. - + Right click one or more entries in your address book and select 'Send message to this address'. Нажмите правую кнопку мыши на каком-либо адресе и выберите "Отправить сообщение на этот адрес". - + Fetched address from namecoin identity. Получить адрес через Namecoin. - + New Message Новое сообщение - + From От - + Sending email gateway registration request Отправка запроса на регистрацию на Email-шлюзе @@ -671,142 +671,142 @@ It is important that you back up this file. Would you like to open the file now? Вы ввели неправильный адрес. Это адрес проигнорирован. - + Error: You cannot add the same address to your address book twice. Try renaming the existing one if you want. Ошибка: Вы не можете добавлять один и тот же адрес в Адресную Книгу несколько раз. Попробуйте переименовать существующий адрес. - + Error: You cannot add the same address to your subscriptions twice. Perhaps rename the existing one if you want. Ошибка: вы не можете добавить один и тот же адрес в ваши подписки дважды. Пожалуйста, переименуйте имеющийся адрес, если хотите. - + Restart Перезапустить - + You must restart Bitmessage for the port number change to take effect. Вы должны перезапустить Bitmessage, чтобы смена номера порта имела эффект. - + Bitmessage will use your proxy from now on but you may want to manually restart Bitmessage now to close existing connections (if any). Bitmessage будет использовать Ваш прокси, начиная прямо сейчас. Тем не менее Вам имеет смысл перезапустить Bitmessage, чтобы закрыть уже существующие соединения. - + Number needed Требуется число - + Your maximum download and upload rate must be numbers. Ignoring what you typed. Скорости загрузки и выгрузки должны быть числами. Игнорирую то, что вы набрали. - + Will not resend ever Не пересылать никогда - + Note that the time limit you entered is less than the amount of time Bitmessage waits for the first resend attempt therefore your messages will never be resent. Обратите внимание, что лимит времени, который вы ввели, меньше чем время, которое Bitmessage ждет перед первой попыткой переотправки сообщения, поэтому ваши сообщения никогда не будут переотправлены. - + Sending email gateway unregistration request Отправка запроса на отмену регистрации на Email-шлюзе - + Sending email gateway status request Отправка запроса статуса аккаунта на Email-шлюзе - + Passphrase mismatch Секретная фраза не подходит - + The passphrase you entered twice doesn't match. Try again. Вы ввели две разные секретные фразы. Пожалуйста, повторите заново. - + Choose a passphrase Придумайте секретную фразу - + You really do need a passphrase. Вы действительно должны ввести секретную фразу. - + Address is gone Адрес утерян - + Bitmessage cannot find your address %1. Perhaps you removed it? Bitmessage не может найти Ваш адрес %1. Возможно Вы удалили его? - + Address disabled Адрес выключен - + Error: The address from which you are trying to send is disabled. You'll have to enable it on the 'Your Identities' tab before using it. Ошибка: адрес, с которого Вы пытаетесь отправить, выключен. Вам нужно будет включить этот адрес во вкладке "Ваши адреса". - + Entry added to the Address Book. Edit the label to your liking. Запись добавлена в Адресную Книгу. Вы можете её отредактировать. - + Entry added to the blacklist. Edit the label to your liking. Запись добавлена в чёрный список. Измените название по своему вкусу. - + Error: You cannot add the same address to your blacklist twice. Try renaming the existing one if you want. Ошибка: вы не можете добавить один и тот же адрес в чёрный список дважды. Попробуйте переименовать существующий адрес. - + Moved items to trash. Удалено в корзину. - + Undeleted item. Отменить удаление записи - + Save As... Сохранить как ... - + Write error. Ошибка записи. - + No addresses selected. Вы не выбрали адрес. - + If you delete the subscription, messages that you already received will become inaccessible. Maybe you can consider disabling the subscription instead. Disabled subscriptions will not receive new messages, but you can still view messages you already received. Are you sure you want to delete the subscription? @@ -815,7 +815,7 @@ Are you sure you want to delete the subscription? Вы уверены, что хотите отменить подписку? - + If you delete the channel, messages that you already received will become inaccessible. Maybe you can consider disabling the channel instead. Disabled channels will not receive new messages, but you can still view messages you already received. Are you sure you want to delete the channel? @@ -824,92 +824,92 @@ Are you sure you want to delete the channel? Вы уверены, что хотите удалить канал? - + Do you really want to remove this avatar? Вы уверены, что хотите удалить этот аватар? - + You have already set an avatar for this address. Do you really want to overwrite it? У вас уже есть аватар для этого адреса. Вы уверены, что хотите перезаписать аватар? - + Start-on-login not yet supported on your OS. Запуск программы при входе в систему ещё не поддерживается в вашей операционной системе. - + Minimize-to-tray not yet supported on your OS. Сворачивание в трей ещё не поддерживается в вашей операционной системе. - + Tray notifications not yet supported on your OS. Уведомления в трее ещё не поддерживаеются в вашей операционной системе. - + Testing... Проверяем... - + This is a chan address. You cannot use it as a pseudo-mailing list. Это адрес chan-а. Вы не можете его использовать как адрес рассылки. - + The address should start with ''BM-'' Адрес должен начинаться с "BM-" - + The address is not typed or copied correctly (the checksum failed). Адрес введен или скопирован неверно (контрольная сумма не сходится). - + The version number of this address is higher than this software can support. Please upgrade Bitmessage. Версия этого адреса более поздняя, чем Ваша программа. Пожалуйста, обновите программу Bitmessage. - + The address contains invalid characters. Адрес содержит запрещённые символы. - + Some data encoded in the address is too short. Данные, закодированные в адресе, слишком короткие. - + Some data encoded in the address is too long. Данные, закодированные в адресе, слишком длинные. - + Some data encoded in the address is malformed. Данные, закодированные в адресе, имеют неверный формат. - + Enter an address above. Введите адрес выше. - + Address is an old type. We cannot display its past broadcasts. Адрес старого типа. Мы не можем отобразить его прошлые рассылки. - + There are no recent broadcasts from this address to display. Нет недавних рассылок с этого адреса для отображения. - + You are using TCP port %1. (This can be changed in the settings). Вы используете TCP порт %1. (Его можно поменять в настройках). @@ -1119,47 +1119,47 @@ Are you sure you want to delete the channel? Добавить новую запись - + Display the %1 recent broadcast(s) from this address. Показать %1 прошлых рассылок с этого адреса. - + New version of PyBitmessage is available: %1. Download it from https://github.com/Bitmessage/PyBitmessage/releases/latest Доступна новая версия PyBitmessage: %1. Загрузите её: https://github.com/Bitmessage/PyBitmessage/releases/latest - + Waiting for PoW to finish... %1% Ожидание окончания PoW... %1% - + Shutting down Pybitmessage... %1% Завершение PyBitmessage... %1% - + Waiting for objects to be sent... %1% Ожидание отправки объектов... %1% - + Saving settings... %1% Сохранение настроек... %1% - + Shutting down core... %1% Завершение работы ядра... %1% - + Stopping notifications... %1% Остановка сервиса уведомлений... %1% - + Shutdown imminent... %1% Завершение вот-вот произойдет... %1% @@ -1169,17 +1169,17 @@ Are you sure you want to delete the channel? %n час%n часа%n часов%n час(а/ов) - + %n day(s) %n день%n дня%n дней%n дней - + Shutting down PyBitmessage... %1% Завершение PyBitmessage... %1% - + Sent Отправленные @@ -1283,7 +1283,7 @@ Receiver's required difficulty: %1 and %2 Проблема: вы пытаетесь отправить сообщение самому себе или в чан, но ваш ключ шифрования не найден в файле ключей keys.dat. Невозможно зашифровать сообщение. %1 - + Doing work necessary to send message. Выполнение работы, требуемой для отправки сообщения. @@ -1293,7 +1293,7 @@ Receiver's required difficulty: %1 and %2 Сообщение отправлено. Ожидаем подтверждения. Отправлено на %1 - + Doing work necessary to request encryption key. Выполнение работы, требуемой для запроса ключа шифрования. @@ -1320,35 +1320,35 @@ Receiver's required difficulty: %1 and %2 Mark all messages as read - Отметить все сообщения прочтенные + Отметить все сообщения как прочтенные - + Are you sure you would like to mark all messages read? Вы уверены, что хотите отметить все сообщения как прочтенные? - + Doing work necessary to send broadcast. Выполнение работы, требуемой для отправки рассылки. - + Proof of work pending Ожидается доказательство работы - + %n object(s) pending proof of work %n объект в ожидании доказательства работы%n объекта в ожидании доказательства работы%n объектов в ожидании доказательства работы%n объектов в ожидании доказательства работы - + %n object(s) waiting to be distributed %n объект ожидает раздачи%n объекта ожидают раздачи%n объектов ожидают раздачи%n объектов ожидают раздачи - + Wait until these tasks finish? Подождать завершения этих задач? @@ -1367,88 +1367,142 @@ Receiver's required difficulty: %1 and %2 The time on your computer, %1, may be wrong. Please verify your settings. Время на компьютере, %1, возможно неправильное. Пожалуйста, проверьте ваши настройки. + + + The name %1 was not found. + Имя %1 не найдено. + + + + The namecoin query failed (%1) + Запрос к namecoin не удался (%1). + + + + The namecoin query failed. + Запрос к namecoin не удался. + + + + The name %1 has no valid JSON data. + Имя %1 не содержит корректных данных JSON. + + + + The name %1 has no associated Bitmessage address. + Имя %1 не имеет связанного адреса Bitmessage. + + + + Success! Namecoind version %1 running. + Успех! Namecoind версии %1 работает. + + + + Success! NMControll is up and running. + Успех! NMControl запущен и работает. + + + + Couldn't understand NMControl. + Не удалось разобрать ответ NMControl. + Your GPU(s) did not calculate correctly, disabling OpenCL. Please report to the developers. Ваша видеокарта вычислила неправильно, отключаем OpenCL. Пожалуйста, сообщите разработчикам. - + + + Welcome to easy and secure Bitmessage + * send messages to other people + * send broadcast messages like twitter or + * discuss in chan(nel)s with other people + + +Добро пожаловать в простой и безопасный Bitmessage +* отправляйте сообщения другим людям +* вещайте, как в twitter или +* участвуйте в обсуждениях в чанах + + + not recommended for chans не рекомендовано для чанов - + Problems connecting? Try enabling UPnP in the Network Settings Проблемы подключения? Попробуйте включить UPnP в сетевых настройках. - + Error: Bitmessage addresses start with BM- Please check the recipient address %1 Ошибка: адреса Bitmessage начинаются с "BM-". Пожалуйста, проверьте адрес получателя %1. - + Error: The recipient address %1 is not typed or copied correctly. Please check it. Ошибка: адрес получателя %1 набран или скопирован неправильно. Пожалуйста, проверьте его. - + Error: The recipient address %1 contains invalid characters. Please check it. Ошибка: адрес получателя %1 содержит недопустимые символы. Пожалуйста, проверьте его. - + Error: The version of the recipient address %1 is too high. Either you need to upgrade your Bitmessage software or your acquaintance is being clever. Ошибка: версия адреса получателя %1 слишком высокая. Либо вам нужно обновить программу Bitmessage, либо ваш знакомый - умник. - + Error: Some data encoded in the recipient address %1 is too short. There might be something wrong with the software of your acquaintance. Ошибка: часть данных, закодированных в адресе получателя %1 слишком короткая. Видимо, что-то не так с программой, используемой вашим знакомым. - + Error: Some data encoded in the recipient address %1 is too long. There might be something wrong with the software of your acquaintance. Ошибка: часть данных, закодированных в адресе получателя %1 слишком длинная. Видимо, что-то не так с программой, используемой вашим знакомым. - + Error: Some data encoded in the recipient address %1 is malformed. There might be something wrong with the software of your acquaintance. Ошибка: часть данных, закодированных в адресе получателя %1 сформирована неправильно. Видимо, что-то не так с программой, используемой вашим знакомым. - + Error: Something is wrong with the recipient address %1. Ошибка: что-то не так с адресом получателя %1. - + Synchronisation pending Ожидается синхронизация - + Bitmessage hasn't synchronised with the network, %n object(s) to be downloaded. If you quit now, it may cause delivery delays. Wait until the synchronisation finishes? Bitmessage не синхронизирован с сетью, незагруженных объектов: %n. Выход сейчас может привести к задержкам доставки. Подождать завершения синхронизации?Bitmessage не синхронизирован с сетью, незагруженных объектов: %n. Выход сейчас может привести к задержкам доставки. Подождать завершения синхронизации?Bitmessage не синхронизирован с сетью, незагруженных объектов: %n. Выход сейчас может привести к задержкам доставки. Подождать завершения синхронизации?Bitmessage не синхронизирован с сетью, незагруженных объектов: %n. Выход сейчас может привести к задержкам доставки. Подождать завершения синхронизации? - + Not connected Не подключено - + Bitmessage isn't connected to the network. If you quit now, it may cause delivery delays. Wait until connected and the synchronisation finishes? Bitmessage не подключен к сети. Выход сейчас может привести к задержкам доставки. Подождать подключения и завершения синхронизации? - + Waiting for network connection... Ожидание сетевого подключения... - + Waiting for finishing synchronisation... Ожидание окончания синхронизации... From f4caf5f6a4d17b9ffee6258ecb3a17957a41cb9b Mon Sep 17 00:00:00 2001 From: PyBitmessage Translations Date: Sat, 4 Mar 2017 14:14:22 +0100 Subject: [PATCH 011/407] Auto-updated language fr from transifex --- src/translations/bitmessage_fr.qm | Bin 94455 -> 95018 bytes src/translations/bitmessage_fr.ts | 551 +++++++++++++++--------------- 2 files changed, 283 insertions(+), 268 deletions(-) diff --git a/src/translations/bitmessage_fr.qm b/src/translations/bitmessage_fr.qm index 1796355d71586cdaf7beb15f2417531d2eb82658..9e97aa12752666d4ea9e40057829d3cf7ffa880f 100644 GIT binary patch delta 3447 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 TH0Z+fdxo+uRYRt;b6x%i?rig7 delta 2983 zcmX9=d0b6-8~^_9x#uqDoO`Yo`#X2CC6v?*5~WmRyJTq*31t$(G^2&O#Y~h;y z08U^7GXP~4u%ry&zXvL90bhH-K>}u-1rn-&;KB6&G$6toY*9+qRD+4P2cND4G6TWy zcncKo1E1#&Y<){O3)oo%zO)!@Rs#5{RIs1}Q0?dF^g*coeZa!HLhDmaw?BqlSO-kl z0t=rPz@L_|IJ5<<+eY+?`v&;_OZ2d+e{C*kxn9Zd0qb5=f3 z^D{;^t_Pm|Zq_k_;vYA&12dSs9Wf*JI2Bshi7kiVpB4!=wHX0R;=zpZ2s}*JjvRqG z(XL=~hGDKMjnZwyd^Hg=aSB!&ih&$E#O$Uh<6RL~I1~ung*D%rh@@n!sr3VLOR+X8 z7i>Z_Hq0*kK;Vv8158X+h&@XzfvhwXS6P91?M3Nk;-{kn2iy!`icM(HrvvXSal1Dq zHt+=QdmRI$|DyFwGf?Wlv;{8#&(+Lww+v=+ntj}hcwZC2`Z!jDja|mk zV=(R^`!tafnW15>7G;!S_I>6#cO>xLE@o_`gC0f}8bles%3%v31Ia^KL>nFKvYkbZ zq6&9jVX5<|!i&q<)pg z9ti(-RMZ_QBxrnxW>d5RrxSX~SV2>}CAM!P2 zHj+Cg{Y07!=FV@Y-?1yXI(0bUSn~lHJh;mnjbug(?r{KBxZ9I!j!gv4bmQJsk@ix9 zmHLmq0ka~UPuiX2tL#=1M3S1PbUR9h^}VR{GE&F>xUclR@~o-wFy*llcd!Ai z%9Bq9flchL{3&h#FlC>z(ee~MSDN1`4JN6Kca0#y6s7ayUQ+t*Ui{=c*GPcf_>e?u zaNc}AEP(jWtl}3BKS?}x@GJ5wD9lDa$+-?}RO2zPtYX8gx zno9xdaHl0SA>OIi?;Zp#m^dzIMQ-3r3H(Q`OpY+nyQbf?YjEA zbdn$aMEEz zch@#%*%Jp|<=Q`HwE?561;I25th=8et|C3+H^FLZBH3W2U=?0R196qmmpvd3J`rr! zUjt*u1^?7i;%1Z(mP_0Xcp`*9nL@&;6e7YX;H*+1@(v~X`0NK{_Ax@@TpGk4SA^V0 z6z@ubQ0D&DM2d43DgxRmqgtU_d`THT6;9MqWnG8_ES<#s(3DG32@3uJa>;0Ea@xW$T5OB9usfI zHG|De5^wo#1qN4&cT9U|Y3de;Pb>!#rJspUY$9pt-IIpB*LeXidDs($e-=pIhiL?) zRY-veOM!uvQrN5cWbrpr__loDri--nz&N6IowRb|F0jcd(igvu2Kr5t;({1(c(JtU zOeV=tl(NT?CW39G?1htnN6#hG{@53^ZR@3SOS-7f4XNCXdU@oaRQ_J?2eYKA6^nr8 zwNed>rkU3N12Vs!(vRCbfz3;#x<`qCPm)w0X#`%(lN$8RK$4qu^VLT*sxL^tuDMFP zCro;|fm*b6icZsXn0oJ{>%EM$bhpJu*MHGz`py`kv(2FZF>`bd<`Up#yKY!o4XN72 zd|Mc5@))dJ+DdUQjMPO{Mv>9i>Qn5_Fd!UGIRGEPt@?0jTR59*9{q-?!x0QtIy+;5x_xO`h4 z+?DQG9V*)!calj4ak6hK?HiHaX)Ny;$BZ=Ty=lVM%gsF1hcT zom>#?0(|{Q-eW_B=G>Mq=8?Jh0J+YQBnEWiz!{zRS6U~wdCE79>wqVk56B!I%Fq1z zlS#%3a=SqWv)!k69MB68eD%&zU(&d>)(<~t0P|j?AKg2fJ|8&!d>8n1@r}-4Fulb^xmS>&*R9C{X68)7ZBW=fD^$*?1o!bZL-){a2 m%rB6ZLD+cng?9gn9=b^x70Y)iy(%8wX^K7i*&KGd$NvGuV2xk^ diff --git a/src/translations/bitmessage_fr.ts b/src/translations/bitmessage_fr.ts index 16d06f3d..0a041f2d 100644 --- a/src/translations/bitmessage_fr.ts +++ b/src/translations/bitmessage_fr.ts @@ -58,12 +58,12 @@ EmailGatewayRegistrationDialog - + Registration failed: L’inscription a échoué : - + The requested email address is not available, please try a new one. Fill out the new desired email address (including @mailchuck.com) below: L’adresse de courriel demandée n’est pas disponible, veuillez en essayer une nouvelle. Saisissez ci-dessous la nouvelle adresse de courriel désirée (incluant @mailchuck.com) : @@ -83,7 +83,7 @@ Veuillez taper l’adresse de courriel désirée (incluant @mailchuck.com) : Mailchuck - + # You can use this to configure your email gateway account # Uncomment the setting you want to use # Here are the options: @@ -173,122 +173,122 @@ Veuillez taper l’adresse de courriel désirée (incluant @mailchuck.com) : MainWindow - + Reply to sender Répondre à l’expéditeur - + Reply to channel Répondre au canal - + Add sender to your Address Book Ajouter l’expéditeur au carnet d’adresses - + Add sender to your Blacklist Ajouter l’expéditeur à votre liste noire - + Move to Trash Envoyer à la Corbeille - + Undelete Restaurer - + View HTML code as formatted text Voir le code HTML comme du texte formaté - + Save message as... Enregistrer le message sous… - + Mark Unread Marquer comme non-lu - + New Nouvelle - + Enable Activer - + Disable Désactiver - + Set avatar... Configurer l’avatar - + Copy address to clipboard Copier l’adresse dans le presse-papier - + Special address behavior... Comportement spécial de l’adresse… - + Email gateway Passerelle de courriel - + Delete Effacer - + Send message to this address Envoyer un message à cette adresse - + Subscribe to this address S’abonner à cette adresse - + Add New Address Ajouter une nouvelle adresse - + Copy destination address to clipboard Copier l’adresse de destination dans le presse-papier - + Force send Forcer l’envoi - + One of your addresses, %1, is an old version 1 address. Version 1 addresses are no longer supported. May we delete it now? Une de vos adresses, %1, est une vieille adresse de la version 1. Les adresses de la version 1 ne sont plus supportées. Nous pourrions la supprimer maintenant? - + Waiting for their encryption key. Will request it again soon. En attente de la clé de chiffrement. Une nouvelle requête sera bientôt lancée. @@ -298,17 +298,17 @@ Veuillez taper l’adresse de courriel désirée (incluant @mailchuck.com) : - + Queued. En attente. - + Message sent. Waiting for acknowledgement. Sent at %1 Message envoyé. En attente de l’accusé de réception. Envoyé %1 - + Message sent. Sent at %1 Message envoyé. Envoyé %1 @@ -318,47 +318,47 @@ Veuillez taper l’adresse de courriel désirée (incluant @mailchuck.com) : - + Acknowledgement of the message received %1 Accusé de réception reçu %1 - + Broadcast queued. Message de diffusion en attente. - + Broadcast on %1 Message de diffusion du %1 - + Problem: The work demanded by the recipient is more difficult than you are willing to do. %1 Problème : Le travail demandé par le destinataire est plus difficile que ce que vous avez paramétré. %1 - + Problem: The recipient's encryption key is no good. Could not encrypt message. %1 Problème : la clé de chiffrement du destinataire n’est pas bonne. Il n’a pas été possible de chiffrer le message. %1 - + Forced difficulty override. Send should start soon. Neutralisation forcée de la difficulté. L’envoi devrait bientôt commencer. - + Unknown status: %1 %2 Statut inconnu : %1 %2 - + Not Connected Déconnecté - + Show Bitmessage Afficher Bitmessage @@ -368,12 +368,12 @@ Veuillez taper l’adresse de courriel désirée (incluant @mailchuck.com) :Envoyer - + Subscribe S’abonner - + Channel Canal @@ -383,12 +383,12 @@ Veuillez taper l’adresse de courriel désirée (incluant @mailchuck.com) :Quitter - + You may manage your keys by editing the keys.dat file stored in the same directory as this program. It is important that you back up this file. Vous pouvez éditer vos clés en éditant le fichier keys.dat stocké dans le même répertoire que ce programme. Il est important de faire des sauvegardes de ce fichier. - + You may manage your keys by editing the keys.dat file stored in %1 It is important that you back up this file. @@ -396,54 +396,54 @@ It is important that you back up this file. Il est important de faire des sauvegardes de ce fichier. - + Open keys.dat? Ouvrir keys.dat ? - + You may manage your keys by editing the keys.dat file stored in the same directory as this program. It is important that you back up this file. Would you like to open the file now? (Be sure to close Bitmessage before making any changes.) Vous pouvez éditer vos clés en éditant le fichier keys.dat stocké dans le même répertoire que ce programme. Il est important de faire des sauvegardes de ce fichier. Souhaitez-vous l’ouvrir maintenant ? (Assurez-vous de fermer Bitmessage avant d’effectuer des changements.) - + You may manage your keys by editing the keys.dat file stored in %1 It is important that you back up this file. Would you like to open the file now? (Be sure to close Bitmessage before making any changes.) Vous pouvez éditer vos clés en éditant le fichier keys.dat stocké dans le répertoire %1. Il est important de faire des sauvegardes de ce fichier. Souhaitez-vous l’ouvrir maintenant? (Assurez-vous de fermer Bitmessage avant d’effectuer des changements.) - + Delete trash? Supprimer la corbeille ? - + Are you sure you want to delete all trashed messages? Êtes-vous sûr de vouloir supprimer tous les messages dans la corbeille ? - + bad passphrase Mauvaise phrase secrète - + You must type your passphrase. If you don't have one then this is not the form for you. Vous devez taper votre phrase secrète. Si vous n’en avez pas, ce formulaire n’est pas pour vous. - + Bad address version number Mauvais numéro de version d’adresse - + Your address version number must be a number: either 3 or 4. Votre numéro de version d’adresse doit être un nombre : soit 3 soit 4. - + Your address version number must be either 3 or 4. Votre numéro de version d’adresse doit être soit 3 soit 4. @@ -513,22 +513,22 @@ It is important that you back up this file. Would you like to open the file now? - + Connection lost Connexion perdue - + Connected Connecté - + Message trashed Message envoyé à la corbeille - + The TTL, or Time-To-Live is the length of time that the network will hold the message. The recipient must get it during this time. If your Bitmessage client does not hear an acknowledgement, it will resend the message automatically. The longer the Time-To-Live, the @@ -537,17 +537,17 @@ It is important that you back up this file. Would you like to open the file now? Le destinataire doit l’obtenir avant ce temps. Si votre client Bitmessage ne reçoit pas de confirmation de réception, il va le ré-envoyer automatiquement. Plus le Time-To-Live est long, plus grand est le travail que votre ordinateur doit effectuer pour envoyer le message. Un Time-To-Live de quatre ou cinq jours est souvent approprié. - + Message too long Message trop long - + The message that you are trying to send is too long by %1 bytes. (The maximum is 261644 bytes). Please cut it down before sending. Le message que vous essayez d’envoyer est trop long de %1 octets (le maximum est 261644 octets). Veuillez le réduire avant de l’envoyer. - + Error: Your account wasn't registered at an email gateway. Sending registration now as %1, please wait for the registration to be processed before retrying sending. Erreur : votre compte n’a pas été inscrit à une passerelle de courrier électronique. Envoi de l’inscription maintenant en tant que %1, veuillez patienter tandis que l’inscription est en cours de traitement, avant de retenter l’envoi. @@ -592,217 +592,217 @@ Le destinataire doit l’obtenir avant ce temps. Si votre client Bitmessage ne r - + Error: You must specify a From address. If you don't have one, go to the 'Your Identities' tab. Erreur : Vous devez spécifier une adresse d’expéditeur. Si vous n’en avez pas, rendez-vous dans l’onglet 'Vos identités'. - + Address version number Numéro de version de l’adresse - + Concerning the address %1, Bitmessage cannot understand address version numbers of %2. Perhaps upgrade Bitmessage to the latest version. Concernant l’adresse %1, Bitmessage ne peut pas comprendre les numéros de version de %2. Essayez de mettre à jour Bitmessage vers la dernière version. - + Stream number Numéro de flux - + Concerning the address %1, Bitmessage cannot handle stream numbers of %2. Perhaps upgrade Bitmessage to the latest version. Concernant l’adresse %1, Bitmessage ne peut pas supporter les nombres de flux de %2. Essayez de mettre à jour Bitmessage vers la dernière version. - + Warning: You are currently not connected. Bitmessage will do the work necessary to send the message but it won't send until you connect. Avertissement : Vous êtes actuellement déconnecté. Bitmessage fera le travail nécessaire pour envoyer le message mais il ne sera pas envoyé tant que vous ne vous connecterez pas. - + Message queued. Message mis en file d’attente. - + Your 'To' field is empty. Votre champ 'Vers' est vide. - + Right click one or more entries in your address book and select 'Send message to this address'. Cliquez droit sur une ou plusieurs entrées dans votre carnet d’adresses et sélectionnez 'Envoyer un message à ces adresses'. - + Fetched address from namecoin identity. Récupération avec succès de l’adresse de l’identité Namecoin. - + New Message Nouveau message - + From De - + Sending email gateway registration request Envoi de la demande d’inscription de la passerelle de courriel - + Address is valid. L’adresse est valide. - + The address you entered was invalid. Ignoring it. L’adresse que vous avez entrée est invalide. Adresse ignorée. - + Error: You cannot add the same address to your address book twice. Try renaming the existing one if you want. Erreur : Vous ne pouvez pas ajouter une adresse déjà présente dans votre carnet d’adresses. Essayez de renommer l’adresse existante. - + Error: You cannot add the same address to your subscriptions twice. Perhaps rename the existing one if you want. Erreur : vous ne pouvez pas ajouter la même adresse deux fois à vos abonnements. Peut-être que vous pouvez renommer celle qui existe si vous le souhaitez. - + Restart Redémarrer - + You must restart Bitmessage for the port number change to take effect. Vous devez redémarrer Bitmessage pour que le changement de port prenne effet. - + Bitmessage will use your proxy from now on but you may want to manually restart Bitmessage now to close existing connections (if any). Bitmessage utilisera votre proxy dorénavant, mais vous pouvez redémarrer manuellement Bitmessage maintenant afin de fermer des connexions existantes (si il y en existe). - + Number needed Nombre requis - + Your maximum download and upload rate must be numbers. Ignoring what you typed. Vos taux maximum de téléchargement et de téléversement doivent être des nombres. Ce que vous avez tapé est ignoré. - + Will not resend ever Ne renverra jamais - + Note that the time limit you entered is less than the amount of time Bitmessage waits for the first resend attempt therefore your messages will never be resent. Notez que la limite de temps que vous avez entrée est plus courte que le temps d’attente respecté par Bitmessage avant le premier essai de renvoi, par conséquent votre message ne sera jamais renvoyé. - + Sending email gateway unregistration request Envoi de la demande de désinscription de la passerelle de courriel - + Sending email gateway status request Envoi à la passerelle de courriel d’une demande de statut - + Passphrase mismatch Phrases secrètes différentes - + The passphrase you entered twice doesn't match. Try again. Les phrases secrètes entrées sont différentes. Réessayez. - + Choose a passphrase Choisissez une phrase secrète - + You really do need a passphrase. Vous devez vraiment utiliser une phrase secrète. - + Address is gone L’adresse a disparu - + Bitmessage cannot find your address %1. Perhaps you removed it? Bitmessage ne peut pas trouver votre adresse %1. Peut-être l’avez-vous supprimée? - + Address disabled Adresse désactivée - + Error: The address from which you are trying to send is disabled. You'll have to enable it on the 'Your Identities' tab before using it. Erreur : L’adresse avec laquelle vous essayez de communiquer est désactivée. Vous devez d’abord l’activer dans l’onglet 'Vos identités' avant de l’utiliser. - + Entry added to the Address Book. Edit the label to your liking. Entrée ajoutée au carnet d’adresse. Éditez l’étiquette à votre convenance. - + Entry added to the blacklist. Edit the label to your liking. Entrée ajoutée à la liste noire. Éditez l’étiquette à votre convenance. - + Error: You cannot add the same address to your blacklist twice. Try renaming the existing one if you want. Erreur : vous ne pouvez pas ajouter la même adresse deux fois à votre liste noire. Essayez de renommer celle qui existe si vous le souhaitez. - + Moved items to trash. Messages déplacés dans la corbeille. - + Undeleted item. Articles restaurés. - + Save As... Enregistrer sous… - + Write error. Erreur d’écriture. - + No addresses selected. Aucune adresse sélectionnée. - + If you delete the subscription, messages that you already received will become inaccessible. Maybe you can consider disabling the subscription instead. Disabled subscriptions will not receive new messages, but you can still view messages you already received. Are you sure you want to delete the subscription? @@ -811,7 +811,7 @@ Are you sure you want to delete the subscription? Êtes-vous sur de vouloir supprimer cet abonnement ? - + If you delete the channel, messages that you already received will become inaccessible. Maybe you can consider disabling the channel instead. Disabled channels will not receive new messages, but you can still view messages you already received. Are you sure you want to delete the channel? @@ -820,92 +820,92 @@ Are you sure you want to delete the channel? Êtes-vous sûr de vouloir supprimer ce canal ? - + Do you really want to remove this avatar? Voulez-vous vraiment enlever cet avatar ? - + You have already set an avatar for this address. Do you really want to overwrite it? Vous avez déjà mis un avatar pour cette adresse. Voulez-vous vraiment l’écraser ? - + Start-on-login not yet supported on your OS. Le démarrage dès l’ouverture de session n’est pas encore supporté sur votre OS. - + Minimize-to-tray not yet supported on your OS. La minimisation en zone système n’est pas encore supportée sur votre OS. - + Tray notifications not yet supported on your OS. Les notifications en zone système ne sont pas encore supportées sur votre OS. - + Testing... Tester… - + This is a chan address. You cannot use it as a pseudo-mailing list. Ceci est une adresse de canal. Vous ne pouvez pas l’utiliser en tant que pseudo liste de diffusion. - + The address should start with ''BM-'' L’adresse devrait commencer avec "BM-" - + The address is not typed or copied correctly (the checksum failed). L’adresse n’est pas correcte (la somme de contrôle a échoué). - + The version number of this address is higher than this software can support. Please upgrade Bitmessage. Le numéro de version de cette adresse est supérieur à celui que le programme peut supporter. Veuiller mettre Bitmessage à jour. - + The address contains invalid characters. L’adresse contient des caractères invalides. - + Some data encoded in the address is too short. Certaines données encodées dans l’adresse sont trop courtes. - + Some data encoded in the address is too long. Certaines données encodées dans l’adresse sont trop longues. - + Some data encoded in the address is malformed. Quelques données codées dans l’adresse sont mal formées. - + Enter an address above. Entrez ci-dessus une adresse. - + Address is an old type. We cannot display its past broadcasts. L’adresse est d’ancien type. Nous ne pouvons pas montrer ses messages de diffusion passés. - + There are no recent broadcasts from this address to display. Il n’y a aucun message de diffusion récent de cette adresse à afficher. - + You are using TCP port %1. (This can be changed in the settings). Vous utilisez le port TCP %1. (Ceci peut être changé dans les paramètres). @@ -1105,57 +1105,57 @@ Are you sure you want to delete the channel? Niveau de zoom %1% - + Error: You cannot add the same address to your list twice. Perhaps rename the existing one if you want. Erreur : vous ne pouvez pas ajouter la même adresse deux fois à votre liste. Vous pouvez peut-être, si vous le souhaitez, renommer celle qui existe déjà. - + Add new entry Ajouter une nouvelle entrée - + Display the %1 recent broadcast(s) from this address. Montre le(s) %1 plus récent(s) message(s) de diffusion issu(s) de cette adresse. - + New version of PyBitmessage is available: %1. Download it from https://github.com/Bitmessage/PyBitmessage/releases/latest Une nouvelle version de PyBitmessage est disponible : %1. Veuillez la télécharger depuis https://github.com/Bitmessage/PyBitmessage/releases/latest - + Waiting for PoW to finish... %1% En attente de la fin de la PoW… %1% - + Shutting down Pybitmessage... %1% Pybitmessage en cours d’arrêt… %1% - + Waiting for objects to be sent... %1% En attente de l’envoi des objets… %1% - + Saving settings... %1% Enregistrement des paramètres… %1% - + Shutting down core... %1% Cœur en cours d’arrêt… %1% - + Stopping notifications... %1% Arrêt des notifications… %1% - + Shutdown imminent... %1% Arrêt imminent… %1% @@ -1165,42 +1165,42 @@ Are you sure you want to delete the channel? %n heure%n heures - + %n day(s) %n jour%n jours - + Shutting down PyBitmessage... %1% PyBitmessage en cours d’arrêt… %1% - + Sent Envoyé - + Generating one new address Production d’une nouvelle adresse - + Done generating address. Doing work necessary to broadcast it... La production de l’adresse a été effectuée. Travail en cours afin de l’émettre… - + Generating %1 new addresses. Production de %1 nouvelles adresses. - + %1 is already in 'Your Identities'. Not adding it again. %1 est déjà dans "Vos identités". Il ne sera pas ajouté de nouveau. - + Done generating address La production d’une adresse a été effectuée @@ -1210,231 +1210,241 @@ Are you sure you want to delete the channel? - + Disk full Disque plein - + Alert: Your disk or data storage volume is full. Bitmessage will now exit. Alerte : votre disque ou le volume de stockage de données est plein. Bitmessage va maintenant se fermer. - + Error! Could not find sender address (your address) in the keys.dat file. Erreur ! Il n’a pas été possible de trouver l’adresse d’expéditeur (votre adresse) dans le fichier keys.dat. - + Doing work necessary to send broadcast... Travail en cours afin d’envoyer le message de diffusion… - + Broadcast sent on %1 Message de diffusion envoyé %1 - + Encryption key was requested earlier. La clé de chiffrement a été demandée plus tôt. - + Sending a request for the recipient's encryption key. Envoi d’une demande de la clé de chiffrement du destinataire. - + Looking up the receiver's public key Recherche de la clé publique du récepteur - + Problem: Destination is a mobile device who requests that the destination be included in the message but this is disallowed in your settings. %1 Problème : la destination est un dispositif mobile qui nécessite que la destination soit incluse dans le message mais ceci n’est pas autorisé dans vos paramètres. %1 - + Doing work necessary to send message. There is no required difficulty for version 2 addresses like this. Travail en cours afin d’envoyer le message. Il n’y a pas de difficulté requise pour les adresses version 2 comme celle-ci. - + Doing work necessary to send message. Receiver's required difficulty: %1 and %2 Travail en cours afin d’envoyer le message. Difficulté requise du destinataire : %1 et %2 - + Problem: The work demanded by the recipient (%1 and %2) is more difficult than you are willing to do. %3 Problème : Le travail demandé par le destinataire (%1 and %2) est plus difficile que ce que vous avez paramétré. %3 - + Problem: You are trying to send a message to yourself or a chan but your encryption key could not be found in the keys.dat file. Could not encrypt message. %1 Problème : Vous essayez d’envoyer un message à un canal ou à vous-même mais votre clef de chiffrement n’a pas été trouvée dans le fichier keys.dat. Le message ne peut pas être chiffré. %1 - + Doing work necessary to send message. Travail en cours afin d’envoyer le message. - + Message sent. Waiting for acknowledgement. Sent on %1 Message envoyé. En attente de l’accusé de réception. Envoyé %1 - + Doing work necessary to request encryption key. Travail en cours afin d’obtenir la clé de chiffrement. - + Broadcasting the public key request. This program will auto-retry if they are offline. Diffusion de la demande de clef publique. Ce programme réessaiera automatiquement si ils sont déconnectés. - + Sending public key request. Waiting for reply. Requested at %1 Envoi d’une demande de clef publique. En attente d’une réponse. Demandée à %1 - + UPnP port mapping established on port %1 Transfert de port UPnP établi sur le port %1 - + UPnP port mapping removed Transfert de port UPnP retiré - + Mark all messages as read Marquer tous les messages comme lus - + Are you sure you would like to mark all messages read? Êtes-vous sûr(e) de vouloir marquer tous les messages comme lus ? - + Doing work necessary to send broadcast. Travail en cours afin d’envoyer la diffusion. - + Proof of work pending En attente de preuve de fonctionnement - + %n object(s) pending proof of work %n objet en attente de preuve de fonctionnement%n objet(s) en attente de preuve de fonctionnement - + %n object(s) waiting to be distributed %n objet en attente d'être distribué%n objet(s) en attente d'être distribués - + Wait until these tasks finish? Attendre jusqu'à ce que ces tâches se terminent ? - + Problem communicating with proxy: %1. Please check your network settings. Problème de communication avec le proxy : %1. Veuillez vérifier vos réglages réseau. - + SOCKS5 Authentication problem: %1. Please check your SOCKS5 settings. Problème d’authentification SOCKS5 : %1. Veuillez vérifier vos réglages SOCKS5. - + The time on your computer, %1, may be wrong. Please verify your settings. L'heure sur votre ordinateur, %1, pourrait être faussse. Veuillez vérifier vos paramètres. - + Your GPU(s) did not calculate correctly, disabling OpenCL. Please report to the developers. Votre GPU(s) n'a pas calculé correctement, mettant OpenCL hors service. Veuillez remonter ceci aux développeurs s'il vous plaît. - + + not recommended for chans + pas recommandé pour les canaux + + + + Problems connecting? Try enabling UPnP in the Network Settings + Des difficultés à se connecter ? Essayez de permettre UPnP dans les "Paramètres réseau" + + + Error: Bitmessage addresses start with BM- Please check the recipient address %1 Erreur : Les adresses Bitmessage commencent par BM- Veuillez vérifier l'adresse du destinataire %1 - + Error: The recipient address %1 is not typed or copied correctly. Please check it. Erreur : L’adresse du destinataire %1 n’est pas correctement tapée ou recopiée. Veuillez la vérifier. - + Error: The recipient address %1 contains invalid characters. Please check it. Erreur : L’adresse du destinataire %1 contient des caractères invalides. Veuillez la vérifier. - + Error: The version of the recipient address %1 is too high. Either you need to upgrade your Bitmessage software or your acquaintance is being clever. Erreur : la version de l’adresse destinataire %1 est trop élevée. Vous devez mettre à niveau votre logiciel Bitmessage ou alors celui de votre connaissance est plus intelligent. - + Error: Some data encoded in the recipient address %1 is too short. There might be something wrong with the software of your acquaintance. Erreur : quelques données codées dans l’adresse destinataire %1 sont trop courtes. Il pourrait y avoir un soucis avec le logiciel de votre connaissance. - + Error: Some data encoded in the recipient address %1 is too long. There might be something wrong with the software of your acquaintance. Erreur : quelques données codées dans l’adresse destinataire %1 sont trop longues. Il pourrait y avoir un soucis avec le logiciel de votre connaissance. - + Error: Some data encoded in the recipient address %1 is malformed. There might be something wrong with the software of your acquaintance. Erreur : quelques données codées dans l’adresse destinataire %1 sont mal formées. Il pourrait y avoir un soucis avec le logiciel de votre connaissance. - + Error: Something is wrong with the recipient address %1. Erreur : quelque chose ne va pas avec l'adresse de destinataire %1. - + Synchronisation pending En attente de synchronisation - + Bitmessage hasn't synchronised with the network, %n object(s) to be downloaded. If you quit now, it may cause delivery delays. Wait until the synchronisation finishes? Bitmessage ne s'est pas synchronisé avec le réseau, %n objet(s) à télécharger. Si vous quittez maintenant, cela pourrait causer des retards de livraison. Attendre jusqu'à ce que la synchronisation aboutisse ?Bitmessage ne s'est pas synchronisé avec le réseau, %n objet(s) à télécharger. Si vous quittez maintenant, cela pourrait causer des retards de livraison. Attendre jusqu'à ce que la synchronisation aboutisse ? - + Not connected Non connecté - + Bitmessage isn't connected to the network. If you quit now, it may cause delivery delays. Wait until connected and the synchronisation finishes? Bitmessage n'est pas connecté au réseau. Si vous quittez maintenant, cela pourrait causer des retards de livraison. Attendre jusqu'à ce qu'il soit connecté et que la synchronisation se termine ? - + Waiting for network connection... En attente de connexion réseau... - + Waiting for finishing synchronisation... En attente d'achèvement de la synchronisation... @@ -1630,27 +1640,27 @@ The 'Random Number' option is selected by default but deterministic ad aboutDialog - + About À propos - + PyBitmessage PyBitmessage - + version ? version ? - + <html><head/><body><p>Distributed under the MIT/X11 software license; see <a href="http://www.opensource.org/licenses/mit-license.php"><span style=" text-decoration: underline; color:#0000ff;">http://www.opensource.org/licenses/mit-license.php</span></a></p></body></html> <html><head/><body><p>Distribué sous la licence logicielle MIT/X11; voir <a href="http://www.opensource.org/licenses/mit-license.php"><span style=" text-decoration: underline; color:#0000ff;">http://www.opensource.org/licenses/mit-license.php</span></a></p></body></html> - + This is Beta software. Version bêta. @@ -1660,7 +1670,7 @@ The 'Random Number' option is selected by default but deterministic ad - + <html><head/><body><p>Copyright &copy; 2012-2016 Jonathan Warren<br/>Copyright &copy; 2013-2016 The Bitmessage Developers</p></body></html> <html><head/><body><p>Copyright &copy; 2012-2016 Jonathan Warren<br/>Copyright &copy; 2013-2016 Les développeurs de Bitmessage</p></body></html> @@ -1693,12 +1703,12 @@ The 'Random Number' option is selected by default but deterministic ad Adresse - + Blacklist Liste noire - + Whitelist Liste blanche @@ -1820,27 +1830,27 @@ The 'Random Number' option is selected by default but deterministic ad Connexions - + Since startup on %1 Démarré depuis le %1 - + Down: %1/s Total: %2 Téléchargées : %1/s Total : %2 - + Up: %1/s Total: %2 Téléversées : %1/s Total : %2 - + Total Connections: %1 Total des connexions : %1 - + Inventory lookups per second: %1 Consultations d’inventaire par seconde : %1 @@ -1860,27 +1870,27 @@ The 'Random Number' option is selected by default but deterministic ad Statut du réseau - + byte(s) octetoctets - + Object(s) to be synced: %n Objet à synchroniser : %nObjets à synchroniser : %n - + Processed %n person-to-person message(s). Traité %n message de personne à personne.Traité %n messages de personne à personne. - + Processed %n broadcast message(s). Traité %n message de diffusion.Traité %n messages de diffusion. - + Processed %n public key(s). Traité %n clé publique.Traité %n clés publiques. @@ -1966,12 +1976,12 @@ The 'Random Number' option is selected by default but deterministic ad Le canal %1 a été rejoint ou créé avec succès. - + Chan creation / joining failed Échec lors de la création du canal ou de la tentative de le rejoindre - + Chan creation / joining cancelled Annulation de la création du canal ou de la tentative de le rejoindre @@ -1979,17 +1989,17 @@ The 'Random Number' option is selected by default but deterministic ad proofofwork - + C PoW module built successfully. Module PoW C construit avec succès. - + Failed to build C PoW module. Please build it manually. Échec à construire le module PoW C. Veuillez le construire manuellement. - + C PoW module unavailable. Please build it. Module PoW C non disponible. Veuillez le construire. @@ -2050,218 +2060,218 @@ The 'Random Number' option is selected by default but deterministic ad settingsDialog - + Settings Paramètres - + Start Bitmessage on user login Démarrer Bitmessage à la connexion de l’utilisateur - + Tray Zone de notification - + Start Bitmessage in the tray (don't show main window) Démarrer Bitmessage dans la barre des tâches (ne pas montrer la fenêtre principale) - + Minimize to tray Minimiser dans la barre des tâches - + Close to tray Fermer vers la zone de notification - + Show notification when message received Montrer une notification lorsqu’un message est reçu - + Run in Portable Mode Lancer en Mode Portable - + In Portable Mode, messages and config files are stored in the same directory as the program rather than the normal application-data folder. This makes it convenient to run Bitmessage from a USB thumb drive. En Mode Portable, les messages et les fichiers de configuration sont stockés dans le même dossier que le programme plutôt que le dossier de l’application. Cela rend l’utilisation de Bitmessage plus facile depuis une clé USB. - + Willingly include unencrypted destination address when sending to a mobile device Inclure volontairement l’adresse de destination non chiffrée lors de l’envoi vers un dispositif mobile - + Use Identicons Utilise des Identicônes. - + Reply below Quote Réponse en dessous de la citation - + Interface Language Langue de l’interface - + System Settings system Paramètres système - + User Interface Interface utilisateur - + Listening port Port d’écoute - + Listen for connections on port: Écouter les connexions sur le port : - + UPnP: UPnP : - + Bandwidth limit Limite de bande passante - + Maximum download rate (kB/s): [0: unlimited] Taux de téléchargement maximal (kO/s) : [0 : illimité] - + Maximum upload rate (kB/s): [0: unlimited] Taux de téléversement maximal (kO/s) : [0 : illimité] - + Proxy server / Tor Serveur proxy / Tor - + Type: Type : - + Server hostname: Nom du serveur: - + Port: Port : - + Authentication Authentification - + Username: Utilisateur : - + Pass: Mot de passe : - + Listen for incoming connections when using proxy Écoute les connexions entrantes lors de l’utilisation du proxy - + none aucun - + SOCKS4a SOCKS4a - + SOCKS5 SOCKS5 - + Network Settings Paramètres réseau - + Total difficulty: Difficulté totale : - + The 'Total difficulty' affects the absolute amount of work the sender must complete. Doubling this value doubles the amount of work. La 'difficulté totale' affecte le montant total de travail que l’envoyeur devra compléter. Doubler cette valeur double la charge de travail. - + Small message difficulty: Difficulté d’un message court : - + When someone sends you a message, their computer must first complete some work. The difficulty of this work, by default, is 1. You may raise this default for new addresses you create by changing the values here. Any new addresses you create will require senders to meet the higher difficulty. There is one exception: if you add a friend or acquaintance to your address book, Bitmessage will automatically notify them when you next send a message that they need only complete the minimum amount of work: difficulty 1. Lorsque quelqu’un vous envoie un message, son ordinateur doit d’abord effectuer un travail. La difficulté de ce travail, par défaut, est de 1. Vous pouvez augmenter cette valeur pour les adresses que vous créez en changeant la valeur ici. Chaque nouvelle adresse que vous créez requerra à l’envoyeur de faire face à une difficulté supérieure. Il existe une exception : si vous ajoutez un ami ou une connaissance à votre carnet d’adresses, Bitmessage les notifiera automatiquement lors du prochain message que vous leur envoyez qu’ils ne doivent compléter que la charge de travail minimale : difficulté 1. - + The 'Small message difficulty' mostly only affects the difficulty of sending small messages. Doubling this value makes it almost twice as difficult to send a small message but doesn't really affect large messages. La 'difficulté d’un message court' affecte principalement la difficulté d’envoyer des messages courts. Doubler cette valeur rend la difficulté à envoyer un court message presque double, tandis qu’un message plus long ne sera pas réellement affecté. - + Demanded difficulty Difficulté exigée - + Here you may set the maximum amount of work you are willing to do to send a message to another person. Setting these values to 0 means that any value is acceptable. Vous pouvez préciser quelle charge de travail vous êtes prêt à effectuer afin d’envoyer un message à une personne. Placer cette valeur à 0 signifie que n’importe quelle valeur est acceptée. - + Maximum acceptable total difficulty: Difficulté maximale acceptée : - + Maximum acceptable small message difficulty: Difficulté maximale acceptée pour les messages courts : - + Max acceptable difficulty Difficulté maximale acceptée @@ -2271,82 +2281,87 @@ The 'Random Number' option is selected by default but deterministic ad - + <html><head/><body><p>Bitmessage can utilize a different Bitcoin-based program called Namecoin to make addresses human-friendly. For example, instead of having to tell your friend your long Bitmessage address, you can simply tell him to send a message to <span style=" font-style:italic;">test. </span></p><p>(Getting your own Bitmessage address into Namecoin is still rather difficult).</p><p>Bitmessage can use either namecoind directly or a running nmcontrol instance.</p></body></html> <html><head/><body><p>Bitmessage peut utiliser Namecoin, un autre programme basé sur Bitcoin, pour avoir des adresses plus parlantes. Par exemple, plutôt que de donner à votre ami votre longue adresse Bitmessage, vous pouvez simplement lui dire d’envoyer un message à <span style=" font-style:italic;">test. </span></p><p>(Obtenir votre propre adresse Bitmessage au sein de Namecoin est encore assez difficile).</p><p>Bitmessage peut soit utiliser directement namecoind soit exécuter une instance de nmcontrol.</p></body></html> - + Host: Hôte : - + Password: Mot de passe : - + Test Test - + Connect to: Connexion à : - + Namecoind Namecoind - + NMControl NMControl - + Namecoin integration Intégration avec Namecoin - + <html><head/><body><p>By default, if you send a message to someone and he is offline for more than two days, Bitmessage will send the message again after an additional two days. This will be continued with exponential backoff forever; messages will be resent after 5, 10, 20 days ect. until the receiver acknowledges them. Here you may change that behavior by having Bitmessage give up after a certain number of days or months.</p><p>Leave these input fields blank for the default behavior. </p></body></html> <html><head/><body><p>Par défaut, si vous envoyez un message à quelqu’un et que cette personne est hors connexion pendant plus de deux jours, Bitmessage enverra le message de nouveau après des deux jours supplémentaires. Ceci sera continué avec reculement (backoff) exponentiel pour toujours; les messages seront réenvoyés après 5, 10, 20 jours etc. jusqu’à ce que le récepteur accuse leur réception. Ici vous pouvez changer ce comportement en faisant en sorte que Bitmessage renonce après un certain nombre de jours ou de mois.</p> <p>Si vous souhaitez obtenir le comportement par défaut alors laissez vides ces champs de saisie. </p></body></html> - + Give up after Abandonner après - + and et - + days jours - + months. mois. - + Resends Expire Expiration des renvois automatiques - + Hide connection notifications Cacher les notifications de connexion - + + Maximum outbound connections: [0: none] + Connexions sortantes maximum: [0: aucune] + + + Hardware GPU acceleration (OpenCL): Accélération matérielle GPU (OpenCL) : From 5c2af003060a479f2ac484fe6210520461e1854a Mon Sep 17 00:00:00 2001 From: PyBitmessage Translations Date: Mon, 6 Mar 2017 22:06:42 +0100 Subject: [PATCH 012/407] Auto-updated language de from transifex --- src/translations/bitmessage_de.qm | Bin 94230 -> 96084 bytes src/translations/bitmessage_de.ts | 393 +++++++++++++++++------------- 2 files changed, 228 insertions(+), 165 deletions(-) diff --git a/src/translations/bitmessage_de.qm b/src/translations/bitmessage_de.qm index 3865eea84079c35a828b50b7f061a019fae15a40..4a74855d7a47ec4a283f69ca93a1174c3d40a899 100644 GIT binary patch delta 4736 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 EmailGatewayRegistrationDialog - + Registration failed: Registrierung fehlgeschlagen: - + The requested email address is not available, please try a new one. Fill out the new desired email address (including @mailchuck.com) below: Die gewünschte E-Mailaddresse ist nicht verfügbar, bitte probieren Sie eine neue. Die gewünschte E-Mailaddresse (inkl. @mailchuck.com) unten ausfüllen: @@ -174,52 +174,52 @@ Bitte geben Sie die gewünschte E-Mail-Adresse (inkl. @mailchuck.com) unten ein: MainWindow - + Reply to sender Dem Absender antworten - + Reply to channel Antworten in den Chan - + Add sender to your Address Book Absender zum Adressbuch hinzufügen - + Add sender to your Blacklist Absender in die Blacklist eintragen - + Move to Trash In den Papierkorb verschieben - + Undelete Wiederherstellen - + View HTML code as formatted text HTML als formatierten Text anzeigen - + Save message as... Nachricht speichern unter... - + Mark Unread Als ungelesen markieren - + New Neu @@ -244,12 +244,12 @@ Bitte geben Sie die gewünschte E-Mail-Adresse (inkl. @mailchuck.com) unten ein: Adresse in die Zwischenablage kopieren - + Special address behavior... Spezielles Verhalten der Adresse... - + Email gateway E-Mail Schnittstelle @@ -259,37 +259,37 @@ Bitte geben Sie die gewünschte E-Mail-Adresse (inkl. @mailchuck.com) unten ein: Löschen - + Send message to this address Nachricht an diese Adresse senden - + Subscribe to this address Diese Adresse abonnieren - + Add New Address Neue Adresse hinzufügen - + Copy destination address to clipboard Zieladresse in die Zwischenablage kopieren - + Force send Senden erzwingen - + One of your addresses, %1, is an old version 1 address. Version 1 addresses are no longer supported. May we delete it now? Eine Ihrer Adressen, %1, ist eine alte Adresse der Version 1 und wird nicht mehr unterstützt. Soll sie jetzt gelöscht werden? - + Waiting for their encryption key. Will request it again soon. Warte auf den Verschlüsselungscode. Wird bald erneut angefordert. @@ -299,17 +299,17 @@ Bitte geben Sie die gewünschte E-Mail-Adresse (inkl. @mailchuck.com) unten ein: - + Queued. In Warteschlange. - + Message sent. Waiting for acknowledgement. Sent at %1 Nachricht gesendet. Warte auf Bestätigung. Zeitpunkt der Sendung: %1 - + Message sent. Sent at %1 Nachricht gesendet. Zeitpunkt der Sendung: %1 @@ -319,47 +319,47 @@ Bitte geben Sie die gewünschte E-Mail-Adresse (inkl. @mailchuck.com) unten ein: - + Acknowledgement of the message received %1 Bestätigung der Nachricht erhalten %1 - + Broadcast queued. Rundruf in Warteschlange. - + Broadcast on %1 Rundruf um %1 - + Problem: The work demanded by the recipient is more difficult than you are willing to do. %1 Problem: Die vom Empfänger geforderte Arbeit ist schwerer als Sie bereit sind, zu berechnen. %1 - + Problem: The recipient's encryption key is no good. Could not encrypt message. %1 Problem: Der Verschlüsselungscode des Empfängers ist nicht in Ordnung. Nachricht konnte nicht verschlüsselt werden. %1 - + Forced difficulty override. Send should start soon. Schwierigkeitslimit überschrieben. Senden sollte bald beginnen. - + Unknown status: %1 %2 Unbekannter Status: %1 %2 - + Not Connected Nicht verbunden - + Show Bitmessage Bitmessage anzeigen @@ -369,12 +369,12 @@ Bitte geben Sie die gewünschte E-Mail-Adresse (inkl. @mailchuck.com) unten ein: Senden - + Subscribe Abonnieren - + Channel Chan @@ -384,12 +384,12 @@ Bitte geben Sie die gewünschte E-Mail-Adresse (inkl. @mailchuck.com) unten ein: Beenden - + You may manage your keys by editing the keys.dat file stored in the same directory as this program. It is important that you back up this file. Sie können Ihre Schlüssel verwalten, indem Sie die keys.dat bearbeiten, die im gleichen Ordner wie das Programm liegt. Es ist empfehlenswert, vorher ein Backup dieser Datei anlegen. - + You may manage your keys by editing the keys.dat file stored in %1 It is important that you back up this file. @@ -398,17 +398,17 @@ It is important that you back up this file. Es ist empfehlenswert, vorher ein Backup dieser Datei anlegen. - + Open keys.dat? Die keys.dat öffnen? - + You may manage your keys by editing the keys.dat file stored in the same directory as this program. It is important that you back up this file. Would you like to open the file now? (Be sure to close Bitmessage before making any changes.) Sie können Ihre Schlüssel verwalten, indem Sie die keys.dat bearbeiten, die im gleichen Ordner wie das Programm liegt. Es ist empfehlenswert, vorher ein Backup dieser Datei anlegen. Möchten Sie die Datei jetzt öffnen? (Stellen Sie sicher, dass Sie Bitmessage beendet haben, bevor Sie etwas ändern.) - + You may manage your keys by editing the keys.dat file stored in %1 It is important that you back up this file. Would you like to open the file now? (Be sure to close Bitmessage before making any changes.) @@ -418,37 +418,37 @@ Es ist empfehlenswert, vorher ein Backup dieser Datei anlegen. Möchten Sie die (Stellen Sie sicher, dass Sie Bitmessage beendet haben, bevor Sie etwas ändern.) - + Delete trash? Papierkorb leeren? - + Are you sure you want to delete all trashed messages? Sind Sie sicher, dass Sie alle Nachrichten im Papierkorb löschen möchten? - + bad passphrase Falsches Passwort - + You must type your passphrase. If you don't have one then this is not the form for you. Sie müssen Ihr Passwort eingeben. Wenn Sie keins haben, ist dies das falsche Formular für Sie. - + Bad address version number Falsche Addressenversionsnummer - + Your address version number must be a number: either 3 or 4. Die Addressenversionsnummer muss eine Zahl sein, entweder 3 oder 4. - + Your address version number must be either 3 or 4. Die Addressenversionnsnummer muss entweder 3 oder 4 sein. @@ -518,22 +518,22 @@ Es ist empfehlenswert, vorher ein Backup dieser Datei anlegen. Möchten Sie die - + Connection lost Verbindung verloren - + Connected Verbunden - + Message trashed Nachricht in den Papierkorb verschoben - + The TTL, or Time-To-Live is the length of time that the network will hold the message. The recipient must get it during this time. If your Bitmessage client does not hear an acknowledgement, it will resend the message automatically. The longer the Time-To-Live, the @@ -541,17 +541,17 @@ Es ist empfehlenswert, vorher ein Backup dieser Datei anlegen. Möchten Sie die Die Haltbarkeit, oder Time-To-Live, ist die Dauer, für die das Netzwerk die Nachricht speichern wird. Der Empfänger muss sie während dieser Zeit empfangen. Wenn Ihr Bitmessage-Client keine Empfangsbestätigung erhält, wird die Nachricht automatisch erneut verschickt. Je länger die Time-To-Live, desto mehr Arbeit muss Ihr Rechner verrichten, um die Nachricht zu senden. Eine Time-To-Live von vier oder fünf Tagen ist meist ausreichend. - + Message too long Narchricht zu lang - + The message that you are trying to send is too long by %1 bytes. (The maximum is 261644 bytes). Please cut it down before sending. Die Nachricht, die Sie zu senden versuchen, ist %1 Byte zu lang. (Maximum 261.644 Bytes). Bitte verringern Sie ihre Größe vor dem Senden. - + Error: Your account wasn't registered at an email gateway. Sending registration now as %1, please wait for the registration to be processed before retrying sending. Fehler: Ihr Konto war an keiner E-Mailschnittstelle registriert. Registrierung als %1 wird versandt, bitte vor einem erneutem Sendeversuch auf die Registrierungsverarbeitung warten. @@ -596,67 +596,67 @@ Es ist empfehlenswert, vorher ein Backup dieser Datei anlegen. Möchten Sie die - + Error: You must specify a From address. If you don't have one, go to the 'Your Identities' tab. Fehler: Sie müssen eine Absenderadresse auswählen. Sollten Sie keine haben, wechseln Sie zum Reiter "Ihre Identitäten". - + Address version number Adressversion - + Concerning the address %1, Bitmessage cannot understand address version numbers of %2. Perhaps upgrade Bitmessage to the latest version. Aufgrund der Adresse %1 kann Bitmessage Adressen mit der Version %2 nicht verarbeiten. Möglicherweise müssen Sie Bitmessage auf die aktuelle Version aktualisieren. - + Stream number Datenstrom Nummer - + Concerning the address %1, Bitmessage cannot handle stream numbers of %2. Perhaps upgrade Bitmessage to the latest version. Aufgrund der Adresse %1 kann Bitmessage den Datenstrom mit der Version %2 nicht verarbeiten. Möglicherweise müssen Sie Bitmessage auf die aktuelle Version aktualisieren. - + Warning: You are currently not connected. Bitmessage will do the work necessary to send the message but it won't send until you connect. Warnung: Sie sind aktuell nicht verbunden. Bitmessage wird die nötige Arbeit zum versenden verrichten, aber erst senden, wenn Sie verbunden sind. - + Message queued. Nachricht befindet sich in der Warteschleife. - + Your 'To' field is empty. Ihr "Empfänger"-Feld ist leer. - + Right click one or more entries in your address book and select 'Send message to this address'. Klicken Sie mit rechts auf einen oder mehrere Einträge aus Ihrem Adressbuch und wählen Sie "Nachricht an diese Adresse senden". - + Fetched address from namecoin identity. Adresse aus Namecoin Identität geholt. - + New Message Neue Nachricht - + From Von - + Sending email gateway registration request Der Registrierungsantrag für die E-Mail Schnittstelle wird versandt. @@ -671,142 +671,142 @@ Es ist empfehlenswert, vorher ein Backup dieser Datei anlegen. Möchten Sie die Die von Ihnen eingegebene Adresse ist ungültig, sie wird ignoriert. - + Error: You cannot add the same address to your address book twice. Try renaming the existing one if you want. Fehler: Sie können eine Adresse nicht doppelt im Adressbuch speichern. Sie können jedoch die bereits eingetragene umbenennen. - + Error: You cannot add the same address to your subscriptions twice. Perhaps rename the existing one if you want. Fehler: Dieselbe Adresse kann nicht doppelt in die Abonnements eingetragen werden. Sie können jedoch die bereits eingetragene umbenennen. - + Restart Neustart - + You must restart Bitmessage for the port number change to take effect. Sie müssen Bitmessage neu starten, um den geänderten Port zu verwenden. - + Bitmessage will use your proxy from now on but you may want to manually restart Bitmessage now to close existing connections (if any). Bitmessage wird ab sofort den Proxy-Server verwenden, aber eventuell möchten Sie Bitmessage neu starten um bereits bestehende Verbindungen zu schließen. - + Number needed Zahl erforderlich - + Your maximum download and upload rate must be numbers. Ignoring what you typed. Ihre maximale Herungerlade- und Hochladegeschwindigkeit müssen Zahlen sein. Die eingetragenen Werte werden ignoriert. - + Will not resend ever Wird nie wiederversendet - + Note that the time limit you entered is less than the amount of time Bitmessage waits for the first resend attempt therefore your messages will never be resent. Bitte beachten Sie, dass der eingetratene Dauer kürzer ist als die, die Bitmessage auf das erste Wiederversenden wartet. Deswegen werden Ihre Nachrichten nie wiederversendet. - + Sending email gateway unregistration request E-Mail Schnittestellen-Abmeldeantrag wird versandt - + Sending email gateway status request E-Mail Schnittestellen Statusantrag wird versandt - + Passphrase mismatch Kennwort stimmt nicht überein - + The passphrase you entered twice doesn't match. Try again. Die von Ihnen eingegebenen Kennwörter sind nicht identisch. Bitte neu versuchen. - + Choose a passphrase Wählen Sie ein Kennwort - + You really do need a passphrase. Sie benötigen wirklich ein Kennwort. - + Address is gone Adresse ist verloren - + Bitmessage cannot find your address %1. Perhaps you removed it? Bitmessage kann Ihre Adresse %1 nicht finden. Haben Sie sie gelöscht? - + Address disabled Adresse deaktiviert - + Error: The address from which you are trying to send is disabled. You'll have to enable it on the 'Your Identities' tab before using it. Fehler: Die Adresse von der Sie versuchen zu senden ist deaktiviert. Sie müssen sie unter dem Reiter "Ihre Identitäten" aktivieren bevor Sie fortfahren. - + Entry added to the Address Book. Edit the label to your liking. Eintrag dem Adressbuch hinzugefügt. Editieren Sie den Eintrag nach Belieben. - + Entry added to the blacklist. Edit the label to your liking. Eintrag in die Blacklist hinzugefügt. Die Beschriftung können Sie ändern. - + Error: You cannot add the same address to your blacklist twice. Try renaming the existing one if you want. Fehler: Dieselbe Addresse kann nicht doppelt in die Blacklist eingetragen werden. Sie können jedoch die bereits eingetragene umbenennen. - + Moved items to trash. Objekt(e) in den Papierkorb verschoben. - + Undeleted item. Nachricht wiederhergestellt. - + Save As... Speichern unter... - + Write error. Fehler beim Speichern. - + No addresses selected. Keine Adresse ausgewählt. - + If you delete the subscription, messages that you already received will become inaccessible. Maybe you can consider disabling the subscription instead. Disabled subscriptions will not receive new messages, but you can still view messages you already received. Are you sure you want to delete the subscription? @@ -815,7 +815,7 @@ Are you sure you want to delete the subscription? Sind Sie sicher, dass Sie das Abonnement löschen möchten? - + If you delete the channel, messages that you already received will become inaccessible. Maybe you can consider disabling the channel instead. Disabled channels will not receive new messages, but you can still view messages you already received. Are you sure you want to delete the channel? @@ -824,92 +824,92 @@ Are you sure you want to delete the channel? Sind Sie sicher, dass Sie das Chan löschen möchten? - + Do you really want to remove this avatar? Wollen Sie diesen Avatar wirklich entfernen? - + You have already set an avatar for this address. Do you really want to overwrite it? Sie haben bereits einen Avatar für diese Adresse gewählt. Wollen Sie ihn wirklich überschreiben? - + Start-on-login not yet supported on your OS. Mit Betriebssystem starten, noch nicht von Ihrem Betriebssystem unterstützt - + Minimize-to-tray not yet supported on your OS. Ins System Tray minimieren von Ihrem Betriebssytem noch nicht unterstützt. - + Tray notifications not yet supported on your OS. Trach-Benachrichtigungen von Ihrem Betriebssystem noch nicht unterstützt. - + Testing... teste... - + This is a chan address. You cannot use it as a pseudo-mailing list. Dies ist eine Chan-Adresse. Sie können sie nicht als Pseudo-Mailingliste verwenden. - + The address should start with ''BM-'' Die Adresse sollte mit "BM-" beginnen - + The address is not typed or copied correctly (the checksum failed). Die Adresse wurde nicht korrekt getippt oder kopiert (Prüfsumme falsch). - + The version number of this address is higher than this software can support. Please upgrade Bitmessage. Die Versionsnummer dieser Adresse ist höher als diese Software unterstützt. Bitte installieren Sie die neuste Bitmessage Version. - + The address contains invalid characters. Diese Adresse beinhaltet ungültige Zeichen. - + Some data encoded in the address is too short. Die in der Adresse codierten Daten sind zu kurz. - + Some data encoded in the address is too long. Die in der Adresse codierten Daten sind zu lang. - + Some data encoded in the address is malformed. Einige in der Adresse kodierten Daten sind ungültig. - + Enter an address above. Eine Addresse oben ausfüllen. - + Address is an old type. We cannot display its past broadcasts. Alter Addressentyp. Wir können deren vorige Rundrufe nicht anzeigen. - + There are no recent broadcasts from this address to display. Es gibt keine neuen Rundrufe von dieser Adresse die angezeigt werden können. - + You are using TCP port %1. (This can be changed in the settings). Sie benutzen TCP-Port %1 (Dieser kann in den Einstellungen verändert werden). @@ -1119,47 +1119,47 @@ Sind Sie sicher, dass Sie das Chan löschen möchten? Neuen Eintrag erstellen - + Display the %1 recent broadcast(s) from this address. Die letzten %1 Rundruf(e) von dieser Addresse anzeigen. - + New version of PyBitmessage is available: %1. Download it from https://github.com/Bitmessage/PyBitmessage/releases/latest Neue Version von PyBitmessage steht zur Verfügung: %1. Sie können sie von https://github.com/Bitmessage/PyBitmessage/releases/latest herunterladen. - + Waiting for PoW to finish... %1% Warte auf Abschluss von Berechnungen (PoW)... %1% - + Shutting down Pybitmessage... %1% PyBitmessage wird beendet... %1% - + Waiting for objects to be sent... %1% Warte auf Versand von Objekten... %1% - + Saving settings... %1% Einstellungen werden gespeichert... %1% - + Shutting down core... %1% Kern wird beendet... %1% - + Stopping notifications... %1% Beende Benachrichtigungen... %1% - + Shutdown imminent... %1% Unmittelbar vor Beendung... %1% @@ -1169,17 +1169,17 @@ Sind Sie sicher, dass Sie das Chan löschen möchten? %n Stunde%n Stunden - + %n day(s) %n Tag%n Tage - + Shutting down PyBitmessage... %1% PyBitmessage wird beendet... %1% - + Sent Gesendet @@ -1224,85 +1224,85 @@ Sind Sie sicher, dass Sie das Chan löschen möchten? Warnung: Datenträger ist voll. Bitmessage wird jetzt beendet. - + Error! Could not find sender address (your address) in the keys.dat file. Fehler! Konnte die Absenderadresse (Ihre Adresse) in der keys.dat-Datei nicht finden. - + Doing work necessary to send broadcast... Arbeit wird verrichtet, um Rundruf zu verschicken... - + Broadcast sent on %1 Rundruf verschickt um %1 - + Encryption key was requested earlier. Verschlüsselungscode wurde früher angefordert. - + Sending a request for the recipient's encryption key. Anfrage nach dem Verschlüsselungscode des Empfängers wird versendet. - + Looking up the receiver's public key Suche nach dem öffentlichen Schlüssel des Empfängers - + Problem: Destination is a mobile device who requests that the destination be included in the message but this is disallowed in your settings. %1 Problem: Der Empfänger benutzt ein mobiles Gerät und erfordert eine unverschlüsselte Empfängeraddresse. Dies ist in Ihren Einstellungen jedoch nicht zulässig. 1% - + Doing work necessary to send message. There is no required difficulty for version 2 addresses like this. Arbeit für Nachrichtenversand wird verrichtet. Version-2-Addressen wie die des Empfängers haben keine Schweirigkeitserforderungen. - + Doing work necessary to send message. Receiver's required difficulty: %1 and %2 Arbeit für Nachrichtenversand wird errichtet. Vom Empfänger geforderte Schwierigkeit: %1 und %2 - + Problem: The work demanded by the recipient (%1 and %2) is more difficult than you are willing to do. %3 Problem: Die vom Empfänger verlangte Arbeit (%1 und %2) ist schwieriger, als Sie in den Einstellungen erlaubt haben. %3 - + Problem: You are trying to send a message to yourself or a chan but your encryption key could not be found in the keys.dat file. Could not encrypt message. %1 Problem: Sie versuchen, eine Nachricht an sich zu versenden, aber Ihr Schlüssel befindet sich nicht in der keys.dat-Datei. Die Nachricht kann nicht verschlüsselt werden. 1% - + Doing work necessary to send message. Arbeit wird verrichtet, um die Nachricht zu verschicken. - + Message sent. Waiting for acknowledgement. Sent on %1 Nachricht gesendet. Auf Bestätigung wird gewartet. Zeitpunkt der Sendung: %1 - + Doing work necessary to request encryption key. Arbeit wird verrichtet, um den Schlüssel nachzufragen... - + Broadcasting the public key request. This program will auto-retry if they are offline. Anfrage nach dem öffentlichen Schlüssel läuft. Wenn der Besitzer nicht mit dem Netzwerk verbunden ist, wird ein Wiederholungsversuch unternommen. - + Sending public key request. Waiting for reply. Requested at %1 Nachfrage nach dem öffentlichen Schlüssel läuft, auf Antwort wird gewartet. Nachgefragt am %1 @@ -1317,37 +1317,37 @@ Receiver's required difficulty: %1 and %2 UPnP Port-Mapping entfernt - + Mark all messages as read Alle Nachrichten als gelesen markieren - + Are you sure you would like to mark all messages read? Sind Sie sicher, dass Sie alle Nachrichten als gelesen markieren möchten? - + Doing work necessary to send broadcast. Führe Arbeit aus, die notwendig ist zum Senden des Rundspruches. - + Proof of work pending Arbeitsbeweis wird berechnet - + %n object(s) pending proof of work %n Objekt wartet auf Berechnungen%n Objekte warten auf Berechnungen - + %n object(s) waiting to be distributed %n Objekt wartet darauf, verteilt zu werden%n Objekte warten darauf, verteilt zu werden - + Wait until these tasks finish? Warten bis diese Aufgaben erledigt sind? @@ -1366,88 +1366,143 @@ Receiver's required difficulty: %1 and %2 The time on your computer, %1, may be wrong. Please verify your settings. Die Uhrzeit ihres Computers, %1, ist möglicherweise falsch. Bitte überprüfen Sie Ihre einstellungen. + + + The name %1 was not found. + Der Name %1 wurde nicht gefunden. + + + + The namecoin query failed (%1) + Namecoin-abfrage fehlgeschlagen (%1) + + + + The namecoin query failed. + Namecoin-abfrage fehlgeschlagen. + + + + The name %1 has no valid JSON data. + Der Name %1 beinhaltet keine gültige JSON-Daten. + + + + The name %1 has no associated Bitmessage address. + Der Name %1 hat keine zugewiesene Bitmessageaddresse. + + + + Success! Namecoind version %1 running. + Erfolg! Namecoind Version %1 läuft. + + + + Success! NMControll is up and running. + Erfolg! NMControl läuft. + + + + Couldn't understand NMControl. + Kann NMControl nicht verstehen. + Your GPU(s) did not calculate correctly, disabling OpenCL. Please report to the developers. Ihre Grafikkarte hat inkorrekt berechnet, OpenCL wird deaktiviert. Bitte benachrichtigen Sie die Entwickler. - + + + Welcome to easy and secure Bitmessage + * send messages to other people + * send broadcast messages like twitter or + * discuss in chan(nel)s with other people + + +Wilkommen zu einfacher und sicherer Bitmessage +* senden Sie Nachrichten an andere Leute +* senden Sie Rundruf-Nachrichte wie bei Twitter oder +* diskutieren Sie mit anderen Leuten in Chans + + + + not recommended for chans für Chans nicht empfohlen - + Problems connecting? Try enabling UPnP in the Network Settings Verbindungsprobleme? Versuchen Sie UPnP in den Netzwerkeinstellungen einzuschalten - + Error: Bitmessage addresses start with BM- Please check the recipient address %1 Fehler: Bitmessage Adressen starten mit BM- Bitte überprüfen Sie die Empfängeradresse %1 - + Error: The recipient address %1 is not typed or copied correctly. Please check it. Fehler: Die Empfängeradresse %1 wurde nicht korrekt getippt oder kopiert. Bitte überprüfen. - + Error: The recipient address %1 contains invalid characters. Please check it. Fehler: Die Empfängeradresse %1 beinhaltet ungültig Zeichen. Bitte überprüfen. - + Error: The version of the recipient address %1 is too high. Either you need to upgrade your Bitmessage software or your acquaintance is being clever. Fehler: Die Empfängerdresseversion von %1 ist zu hoch. Entweder Sie müssen Ihre Bitmessage Software aktualisieren oder Ihr Bekannter ist sehr clever. - + Error: Some data encoded in the recipient address %1 is too short. There might be something wrong with the software of your acquaintance. Fehler: Einige Daten die in der Empfängerdresse %1 codiert sind, sind zu kurz. Es könnte sein, dass etwas mit der Software Ihres Bekannten nicht stimmt. - + Error: Some data encoded in the recipient address %1 is too long. There might be something wrong with the software of your acquaintance. Fehler: Einige Daten die in der Empfängeradresse %1 codiert sind, sind zu lang. Es könnte sein, dass etwas mit der Software Ihres Bekannten nicht stimmt. - + Error: Some data encoded in the recipient address %1 is malformed. There might be something wrong with the software of your acquaintance. Fehler: Einige codierte Daten in der Empfängeradresse %1 sind ungültig. Es könnte etwas mit der Software Ihres Bekannten sein. - + Error: Something is wrong with the recipient address %1. Fehler: Mit der Empfängeradresse %1 stimmt etwas nicht. - + Synchronisation pending Synchronisierung läuft - + Bitmessage hasn't synchronised with the network, %n object(s) to be downloaded. If you quit now, it may cause delivery delays. Wait until the synchronisation finishes? Bitmessage ist nicht synchronisiert, %n Objekt wurde noch nicht heruntergeladen. Wenn Sie das Programm jetzt beenden, kann es zu Zustellverzögerungen kommen. Warten bis die Synchronisierung abgeschlossen ist?Bitmessage ist nicht synchronisiert, %n Objekte wurden noch nicht heruntergeladen. Wenn Sie das Programm jetzt beenden, kann es zu Zustellverzögerungen kommen. Warten bis die Synchronisierung abgeschlossen ist? - + Not connected Nicht verbunden - + Bitmessage isn't connected to the network. If you quit now, it may cause delivery delays. Wait until connected and the synchronisation finishes? Bitmessage ist nicht mit dem Netzwerk verbunden. Wenn Sie das Programm jetzt beenden, kann es zu Zustellverzögerungen kommen. Warten bis eine Verbindung besteht und die Synchronisierung abgeschlossen ist? - + Waiting for network connection... Warte auf Netzwerkverbindung... - + Waiting for finishing synchronisation... Warte auf Synchronisationsabschluss... @@ -2008,6 +2063,14 @@ Die Zufallszahlen-Option ist standardmässig gewählt, jedoch haben deterministi C-PoW-Modul nicht verfügbar. Bitte erstellen Sie es. + + qrcodeDialog + + + QR-code + QR-Code + + regenerateAddressesDialog From 5d545dbcab5ec8920f380054088027d94cf4d6c9 Mon Sep 17 00:00:00 2001 From: PyBitmessage Translations Date: Mon, 6 Mar 2017 22:16:29 +0100 Subject: [PATCH 013/407] Auto-updated language sk from transifex --- src/translations/bitmessage_sk.qm | Bin 86876 -> 88644 bytes src/translations/bitmessage_sk.ts | 393 +++++++++++++++++------------- 2 files changed, 228 insertions(+), 165 deletions(-) diff --git a/src/translations/bitmessage_sk.qm b/src/translations/bitmessage_sk.qm index 89a52107cdea3baef18e46e93b4e416079b0c4b0..9a3e3670bf05e692e2084bef71abbc91bd73c4c9 100644 GIT binary patch delta 4821 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 delta 3315 zcmX9=c|c8h8-9NGo^$U#_nv$2RLmGYX|&kdM_N#*5W2>em{cRpM4O#Nx27gZ6fG(# zOS0BTStB9Fpe#|ULGvj~L(JHHroNZ{dhhR?d*1bVp5I$t&i_%wr}fb91z-oJSqgBQ zfPf1C|0__q92jp23_1x+{|#8|4tzhDp6>xdJit6JfV*xE1ik>*I0u+J0o+qtAc&rS zvK z1kV!`I2@XtwZIrX3_M=~w?Dw3bTiPEjowikf$c#st?LB*@?fEG1$Gu=NN@Uoo((K7 z_XV>NV0V$uQ7y3Fln)%pfMb0;&}4++Uv)r_Io0qiOaQ{}V_a4>(7&|XwwA%`@if5k z6}&TFP=)dE&Qg*s#^eR7zy@-dy7CltZ)3M@u7FSS0xb=9s%+az{3j5jSKa_`qfxbK$(r6f~cL=z?4`-7t!;=S`1$Y>X#>I6& z+%w$mLkVhjRTN5rb<^4DZ}$O`;%+UW(4AY zBn@lMgV~*vG--YSI{zbi$SnbyKkB7o3J>PGSgO8p7#Qdx)&4@W`Xf$iboc?$r9oE2RVrr4AxFR#Z!eMaF>juab_`nSiBpu+LEp4Akm2R?=cC3s6%3es{9Vd{kzQt+#ZUjE=;j{sSa;sAA%l)$m zd{?-U$4DchmV6SC-cU*ptVd-0un| zzUIz89Yle%x#}o$V4M+GZ+MR0kKvlmyd$_x`6kOA#7|6<>$?$*~K5{e{?tt4D;Y4^Qon-JRfUc3)Xiv zA0HbGHZ+#cb{+&=DCcuuE&(%Y=C>wMoTO!Z$vPfzGULlSp%Cl^R?@V zpH>RKecWW?{}cX=lqwHf%fIz7AYp2d87v@2j#p)Yi)oe}(X#L#BS?~)Ws$e-fg$Cx zWog8sp+{s%%V{*evt@Y`+Q2+^%L@OvMhfUD+nY`!>32s~JcJ6HW*{rus5b)cc*%}^ znssZF?9v6-+!!$~G#Z|eZvKIK)MDFs#0O03p zxpyO-*DjX(q^$uOx5$I-=96G0%i{~&$lt&6#7SR+4GWTQbhrmh66I-M?jjn-%hU92 zI>74-d1f6|^xX-0ZfFTGt65&KgDM!^Cf^yAKp=9IA4nlKz^B`qKkK&52Hp0dOnz)4 zeV_G0e$F8u%;lN02aQA6`t0lfZG+xyJiy@4=OedCiGT6P^4FBfK#Uwr+i0K1$By3 z6|RJZUW&_mt4WGqD6Z&@0W&*AQ(^>=mZ)gTp|j6ZMeALExRWQW^!>Q;aJ zvn`aExr2&%mk|H6N>xG>q2Kj_N-4cT85yaRtxKu$29-LF77^jDs#h+}(xO;p@v0IS z@K`nQv_EY}gH^V&7@*Qp$7EsHmWII`>3#0s-S4X zOy_ge`c?5{PpHz)H0deh8dYwcI|;;hsytyRfhk^fDRw?kI!$%yza_xd{i=o>9oUyl z)fm-5{I^pzjnAY+wyRop5Pe@atDYJX4cnttPfbWaOwWk6pU}VQzBtN~I#?efdK{4g zYaWWeYeET}_2RtO0VM77#NaG_K5%ih7+O4rS{p1b^x8%%(+P2D^KifkD+i-G1T?Z3o@giDKhx+TE zv@)F*U#+DPC27=(hev3JyVZTd2uw8()Mi2FX-R#ow#cCXVQy-x^!>oSgKFF43c|=G zwey=wx>YmPBMe=E3B1~4mKQC$PY=e+7HgDSGlJWMxxcLSCgZ61J(I=YJu~G>iWypL_;HWYwBDYiQ_~~NoOGeT#&iswPe5j!ktJ37{(v@aEPancZE8~~uue@!RYS1Yw8<21mZ z^R>tO<&%(EWc@d#)$hf-3FJ2y!%Tg0^iW@p#>D+7BBNXbBCVn`Ufky48D+ kG9UFN EmailGatewayRegistrationDialog - + Registration failed: Registrácia zlyhala: - + The requested email address is not available, please try a new one. Fill out the new desired email address (including @mailchuck.com) below: Požadovaná e-mailová adresa nie je k dispozícii, skúste znova. Vyplňte novú požadovanú e-mailovú adresu (vrátane @mailchuck.com) nižšie: @@ -171,52 +171,52 @@ Vyplňte požadovanú e-mailovú adresu (vrátane @mailchuck.com) nižšie: MainWindow - + Reply to sender Odpovedať odosielateľovi - + Reply to channel Odpoveď na kanál - + Add sender to your Address Book Pridať odosielateľa do adresára - + Add sender to your Blacklist Pridať odosielateľa do svojho zoznamu zakázaných - + Move to Trash Presunúť do koša - + Undelete Obnoviť - + View HTML code as formatted text Zobraziť HTML kód ako formátovaný text - + Save message as... Uložiť správu ako... - + Mark Unread Označiť ako neprečítané - + New Nová @@ -241,12 +241,12 @@ Vyplňte požadovanú e-mailovú adresu (vrátane @mailchuck.com) nižšie:Kopírovať adresu do clipboardu - + Special address behavior... Zvláštne správanie adresy... - + Email gateway E-mailová brána @@ -256,37 +256,37 @@ Vyplňte požadovanú e-mailovú adresu (vrátane @mailchuck.com) nižšie:Zmazať - + Send message to this address Poslať správu na túto adresu - + Subscribe to this address Prihlásiť sa k odberu tejto adresy - + Add New Address Pridať novú adresu - + Copy destination address to clipboard Kopírovať cieľovú adresu do clipboardu - + Force send Vynútiť odoslanie - + One of your addresses, %1, is an old version 1 address. Version 1 addresses are no longer supported. May we delete it now? Jedna z vašich adries, %1, je stará verzia adresy, 1. Verzie adresy 1 už nie sú podporované. Odstrániť ju teraz? - + Waiting for their encryption key. Will request it again soon. Čakanie na šifrovací kľúč príjemcu. Čoskoro bude vyžiadaný znova. @@ -296,17 +296,17 @@ Vyplňte požadovanú e-mailovú adresu (vrátane @mailchuck.com) nižšie: - + Queued. Vo fronte. - + Message sent. Waiting for acknowledgement. Sent at %1 Správa odoslaná. Čakanie na potvrdenie. Odoslaná %1 - + Message sent. Sent at %1 Správa odoslaná. Odoslaná %1 @@ -316,47 +316,47 @@ Vyplňte požadovanú e-mailovú adresu (vrátane @mailchuck.com) nižšie: - + Acknowledgement of the message received %1 Potvrdenie prijatia správy %1 - + Broadcast queued. Rozoslanie vo fronte. - + Broadcast on %1 Rozoslané 1% - + Problem: The work demanded by the recipient is more difficult than you are willing to do. %1 Problém: práca požadovná príjemcom je oveľa ťažšia, než je povolené v nastaveniach. %1 - + Problem: The recipient's encryption key is no good. Could not encrypt message. %1 Problém: šifrovací kľúč príjemcu je nesprávny. Nie je možné zašifrovať správu. %1 - + Forced difficulty override. Send should start soon. Obmedzenie obtiažnosti práce zrušené. Odosielanie by malo čoskoro začať. - + Unknown status: %1 %2 Neznámy stav: %1 %2 - + Not Connected Nepripojený - + Show Bitmessage Ukázať Bitmessage @@ -366,12 +366,12 @@ Vyplňte požadovanú e-mailovú adresu (vrátane @mailchuck.com) nižšie:Odoslať - + Subscribe Prihlásiť sa k odberu - + Channel Kanál @@ -381,12 +381,12 @@ Vyplňte požadovanú e-mailovú adresu (vrátane @mailchuck.com) nižšie:Ukončiť - + You may manage your keys by editing the keys.dat file stored in the same directory as this program. It is important that you back up this file. Kľúče môžete spravovať úpravou súboru keys.dat, ktorý je uložený v rovnakom adresári ako tento program. Tento súbor je dôležité zálohovať. - + You may manage your keys by editing the keys.dat file stored in %1 It is important that you back up this file. @@ -395,17 +395,17 @@ It is important that you back up this file. Tento súbor je dôležité zálohovať. - + Open keys.dat? Otvoriť keys.dat? - + You may manage your keys by editing the keys.dat file stored in the same directory as this program. It is important that you back up this file. Would you like to open the file now? (Be sure to close Bitmessage before making any changes.) Kľúče môžete spravovať úpravou súboru keys.dat, ktorý je uložený v rovnakom adresári ako tento program. Tento súbor je dôležité zálohovať. Chcete tento súbor teraz otvoriť? (Nezabudnite zatvoriť Bitmessage pred vykonaním akýchkoľvek zmien.) - + You may manage your keys by editing the keys.dat file stored in %1 It is important that you back up this file. Would you like to open the file now? (Be sure to close Bitmessage before making any changes.) @@ -414,37 +414,37 @@ It is important that you back up this file. Would you like to open the file now? Tento súbor je dôležité zálohovať. Chcete tento súbor teraz otvoriť? (Nezabudnite zatvoriť Bitmessage pred vykonaním akýchkoľvek zmien.) - + Delete trash? Vyprázdniť kôš? - + Are you sure you want to delete all trashed messages? Ste si istí, že chcete všetky správy z koša odstrániť? - + bad passphrase zlé heslo - + You must type your passphrase. If you don't have one then this is not the form for you. Je nutné zadať prístupové heslo. Ak heslo nemáte, tento formulár nie je pre Vás. - + Bad address version number Nesprávne číslo verzie adresy - + Your address version number must be a number: either 3 or 4. Číslo verzie adresy musí byť číslo: buď 3 alebo 4. - + Your address version number must be either 3 or 4. Vaše číslo verzie adresy musí byť buď 3 alebo 4. @@ -514,22 +514,22 @@ Tento súbor je dôležité zálohovať. Chcete tento súbor teraz otvoriť? (Ne - + Connection lost Spojenie bolo stratené - + Connected Spojený - + Message trashed Správa odstránenia - + The TTL, or Time-To-Live is the length of time that the network will hold the message. The recipient must get it during this time. If your Bitmessage client does not hear an acknowledgement, it will resend the message automatically. The longer the Time-To-Live, the @@ -537,17 +537,17 @@ Tento súbor je dôležité zálohovať. Chcete tento súbor teraz otvoriť? (Ne TTL (doba životnosti) je čas, počas ktorého bude sieť udržiavať správu. Príjemca musí správu prijať počas tejto životnosti. Keď odosielateľov Bitmessage nedostane po vypršaní životnosti potvrdenie o prijatí, automaticky správu odošle znova. Čím vyššia doba životnosti, tým viac práce musí počítač odosielateľa vykonat na odoslanie správy. Zvyčajne je vhodná doba životnosti okolo štyroch-piatich dní. - + Message too long Správa je príliš dlhá - + The message that you are trying to send is too long by %1 bytes. (The maximum is 261644 bytes). Please cut it down before sending. Správa, ktorú skúšate poslať, má %1 bajtov naviac. (Maximum je 261 644 bajtov). Prosím pred odoslaním skrátiť. - + Error: Your account wasn't registered at an email gateway. Sending registration now as %1, please wait for the registration to be processed before retrying sending. Chyba: Váš účet nebol registrovaný na e-mailovej bráne. Skúšam registrovať ako %1, prosím počkajte na spracovanie registrácie pred opakovaným odoslaním správy. @@ -592,67 +592,67 @@ Tento súbor je dôležité zálohovať. Chcete tento súbor teraz otvoriť? (Ne - + Error: You must specify a From address. If you don't have one, go to the 'Your Identities' tab. Chyba: musíte zadať adresu "Od". Ak žiadnu nemáte, prejdite na kartu "Vaše identity". - + Address version number Číslo verzie adresy - + Concerning the address %1, Bitmessage cannot understand address version numbers of %2. Perhaps upgrade Bitmessage to the latest version. Čo sa týka adresy %1, Bitmessage nepozná číslo verzie adresy %2. Možno by ste mali upgradenúť Bitmessage na najnovšiu verziu. - + Stream number Číslo prúdu - + Concerning the address %1, Bitmessage cannot handle stream numbers of %2. Perhaps upgrade Bitmessage to the latest version. Čo sa týka adresy %1, Bitmessage nespracováva číslo prúdu %2. Možno by ste mali upgradenúť Bitmessage na najnovšiu verziu. - + Warning: You are currently not connected. Bitmessage will do the work necessary to send the message but it won't send until you connect. Upozornenie: momentálne nie ste pripojení. Bitmessage vykoná prácu potrebnú na odoslanie správy, ale odoslať ju môže, až keď budete pripojení. - + Message queued. Správa vo fronte. - + Your 'To' field is empty. Pole "Komu" je prázdne. - + Right click one or more entries in your address book and select 'Send message to this address'. Vybertie jednu alebo viacero položiek v adresári, pravým tlačidlom myši zvoľte "Odoslať správu na túto adresu". - + Fetched address from namecoin identity. Prebratá adresa z namecoin-ovej identity. - + New Message Nová správa - + From Od - + Sending email gateway registration request Odosielam požiadavku o registráciu na e-mailovej bráne @@ -667,142 +667,142 @@ Tento súbor je dôležité zálohovať. Chcete tento súbor teraz otvoriť? (Ne Zadaná adresa bola neplatná a bude ignorovaná. - + Error: You cannot add the same address to your address book twice. Try renaming the existing one if you want. Chyba: tú istú adresu nemožno pridať do adresára dvakrát. Ak chcete, môžete skúsiť premenovať existujúcu menovku. - + Error: You cannot add the same address to your subscriptions twice. Perhaps rename the existing one if you want. Chyba: nemožno pridať rovnakú adresu k odberu dvakrát. Keď chcete, môžete premenovať existujúci záznam. - + Restart Reštart - + You must restart Bitmessage for the port number change to take effect. Aby sa zmena čísla portu prejavila, musíte reštartovať Bitmessage. - + Bitmessage will use your proxy from now on but you may want to manually restart Bitmessage now to close existing connections (if any). Bitmessage bude odteraz používať proxy, ale ak chcete ukončiť existujúce spojenia, musíte Bitmessage manuálne reštartovať. - + Number needed Číslo potrebné - + Your maximum download and upload rate must be numbers. Ignoring what you typed. Maxímálna rýchlosť príjmu a odoslania musí byť uvedená v číslach. Ignorujem zadané údaje. - + Will not resend ever Nikdy opätovne neodosielať - + Note that the time limit you entered is less than the amount of time Bitmessage waits for the first resend attempt therefore your messages will never be resent. Upozornenie: časový limit, ktorý ste zadali, je menší ako čas, ktorý Bitmessage čaká na prvý pokus o opätovné zaslanie, a preto vaše správy nebudú nikdy opätovne odoslané. - + Sending email gateway unregistration request Odosielam žiadosť o odhlásenie z e-mailovej brány - + Sending email gateway status request Odosielam požiadavku o stave e-mailovej brány - + Passphrase mismatch Nezhoda hesla - + The passphrase you entered twice doesn't match. Try again. Zadané heslá sa rôznia. Skúste znova. - + Choose a passphrase Vyberte heslo - + You really do need a passphrase. Heslo je skutočne potrebné. - + Address is gone Adresa zmizla - + Bitmessage cannot find your address %1. Perhaps you removed it? Bitmessage nemôže nájsť vašu adresu %1. Možno ste ju odstránili? - + Address disabled Adresa deaktivovaná - + Error: The address from which you are trying to send is disabled. You'll have to enable it on the 'Your Identities' tab before using it. Chyba: adresa, z ktorej sa pokúšate odoslať, je neaktívna. Pred použitím ju musíte aktivovať v karte "Vaše identity". - + Entry added to the Address Book. Edit the label to your liking. Záznam pridaný do adresára. Upravte označenie podľa vašich predstáv. - + Entry added to the blacklist. Edit the label to your liking. Záznam pridaný na zoznam zakázaných. Upravte označenie podľa vašich predstáv. - + Error: You cannot add the same address to your blacklist twice. Try renaming the existing one if you want. Chyba: tú istú adresu nemožno pridať na zoznam zakázaných dvakrát. Ak chcete, môžete skúsiť premenovať existujúce označenie. - + Moved items to trash. Položky presunuté do koša. - + Undeleted item. Položka obnovená. - + Save As... Uložiť ako... - + Write error. Chyba pri zapisovaní. - + No addresses selected. Nevybraná žiadna adresa. - + If you delete the subscription, messages that you already received will become inaccessible. Maybe you can consider disabling the subscription instead. Disabled subscriptions will not receive new messages, but you can still view messages you already received. Are you sure you want to delete the subscription? @@ -811,7 +811,7 @@ Are you sure you want to delete the subscription? Ste si istý, že chcete odber odstrániť? - + If you delete the channel, messages that you already received will become inaccessible. Maybe you can consider disabling the channel instead. Disabled channels will not receive new messages, but you can still view messages you already received. Are you sure you want to delete the channel? @@ -820,92 +820,92 @@ Are you sure you want to delete the channel? Ste si istý, že chcete kanál odstrániť? - + Do you really want to remove this avatar? Naozaj chcete odstrániť tento avatar? - + You have already set an avatar for this address. Do you really want to overwrite it? Pre túto adresu ste už ste nastavili avatar. Naozaj ho chcete ho zmeniť? - + Start-on-login not yet supported on your OS. Spustenie pri prihlásení zatiaľ pre váš operačný systém nie je podporované. - + Minimize-to-tray not yet supported on your OS. Minimalizovanie do panelu úloh zatiaľ pre váš operačný systém nie je podporované. - + Tray notifications not yet supported on your OS. Oblasť oznámení zatiaľ pre váš operačný systém nie je podporovaná. - + Testing... Testujem... - + This is a chan address. You cannot use it as a pseudo-mailing list. Toto je adresa kanálu. Nie je možné ju používať ako pseudo poštový zoznam. - + The address should start with ''BM-'' Adresa by mala začínať ''BM-'' - + The address is not typed or copied correctly (the checksum failed). Nesprávne zadaná alebo skopírovaná adresa (kontrolný súčet zlyhal). - + The version number of this address is higher than this software can support. Please upgrade Bitmessage. Číslo verzie tejto adresy je vyššie ako tento softvér podporuje. Prosím inovujte Bitmessage. - + The address contains invalid characters. Adresa obsahuje neplatné znaky. - + Some data encoded in the address is too short. Niektoré dáta zakódované v adrese sú príliš krátke. - + Some data encoded in the address is too long. Niektoré dáta zakódované v adrese sú príliš dlhé. - + Some data encoded in the address is malformed. Niektoré dáta zakódované v adrese sú poškodené. - + Enter an address above. Zadajte adresu vyššie. - + Address is an old type. We cannot display its past broadcasts. Starý typ adresy. Nie je možné zobraziť jej predchádzajúce hromadné správy. - + There are no recent broadcasts from this address to display. Neboli nájdené žiadne nedávne hromadé správy z tejto adresy. - + You are using TCP port %1. (This can be changed in the settings). Používate port TCP %1. (Možno zmeniť v nastaveniach). @@ -1115,47 +1115,47 @@ Ste si istý, že chcete kanál odstrániť? Pridať nový záznam - + Display the %1 recent broadcast(s) from this address. Zobraziť posledných %1 hromadných správ z tejto adresy. - + New version of PyBitmessage is available: %1. Download it from https://github.com/Bitmessage/PyBitmessage/releases/latest K dispozícii je nová verzia PyBitmessage: %1. Môžete ju stiahnuť na https://github.com/Bitmessage/PyBitmessage/releases/latest - + Waiting for PoW to finish... %1% Čakám na ukončenie práce... %1% - + Shutting down Pybitmessage... %1% Ukončujem PyBitmessage... %1% - + Waiting for objects to be sent... %1% Čakám na odoslanie objektov... %1% - + Saving settings... %1% Ukladám nastavenia... %1% - + Shutting down core... %1% Ukončujem jadro... %1% - + Stopping notifications... %1% Zastavujem oznámenia... %1% - + Shutdown imminent... %1% Posledná fáza ukončenia... %1% @@ -1165,17 +1165,17 @@ Ste si istý, že chcete kanál odstrániť? %n hodina%n hodiny%n hodín - + %n day(s) %n deň%n dni%n dní - + Shutting down PyBitmessage... %1% Ukončujem PyBitmessage... %1% - + Sent Odoslané @@ -1220,86 +1220,86 @@ Ste si istý, že chcete kanál odstrániť? Upozornenie: Váš disk alebo priestor na ukladanie dát je plný. Bitmessage bude teraz ukončený. - + Error! Could not find sender address (your address) in the keys.dat file. Chyba! Nemožno nájsť adresu odosielateľa (vašu adresu) v súbore keys.dat. - + Doing work necessary to send broadcast... Vykonávam prácu potrebnú na rozoslanie... - + Broadcast sent on %1 Rozoslané %1 - + Encryption key was requested earlier. Šifrovací klúč bol vyžiadaný. - + Sending a request for the recipient's encryption key. Odosielam požiadavku na kľúč príjemcu. - + Looking up the receiver's public key Hľadám príjemcov verejný kľúč - + Problem: Destination is a mobile device who requests that the destination be included in the message but this is disallowed in your settings. %1 Problém: adresa príjemcu je na mobilnom zariadení a požaduje, aby správy obsahovali nezašifrovanú adresu príjemcu. Vaše nastavenia však túto možnost nemajú povolenú. %1 - + Doing work necessary to send message. There is no required difficulty for version 2 addresses like this. Vykonávam prácu potrebnú na odoslanie správy. Adresy verzie dva, ako táto, nepožadujú obtiažnosť. - + Doing work necessary to send message. Receiver's required difficulty: %1 and %2 Vykonávam prácu potrebnú na odoslanie správy. Priímcova požadovaná obtiažnosť: %1 a %2 - + Problem: The work demanded by the recipient (%1 and %2) is more difficult than you are willing to do. %3 Problém: Práca požadovná príjemcom (%1 a %2) je obtiažnejšia, ako máte povolené. %3 - + Problem: You are trying to send a message to yourself or a chan but your encryption key could not be found in the keys.dat file. Could not encrypt message. %1 Problém: skúšate odslať správu sami sebe, ale nemôžem nájsť šifrovací kľúč v súbore keys.dat. Nemožno správu zašifrovať: %1 - + Doing work necessary to send message. Vykonávam prácu potrebnú na odoslanie... - + Message sent. Waiting for acknowledgement. Sent on %1 Správa odoslaná. Čakanie na potvrdenie. Odoslaná %1 - + Doing work necessary to request encryption key. Vykonávam prácu potrebnú na vyžiadanie šifrovacieho kľúča. - + Broadcasting the public key request. This program will auto-retry if they are offline. Rozosielam požiadavku na verejný kľúč. Ak nebude príjemca spojený zo sieťou, budem skúšať znova. - + Sending public key request. Waiting for reply. Requested at %1 Odosielam požiadavku na verejný kľúč. Čakám na odpoveď. Vyžiadaný %1 @@ -1314,37 +1314,37 @@ Priímcova požadovaná obtiažnosť: %1 a %2 Mapovanie portov UPnP zrušené - + Mark all messages as read Označiť všetky správy ako prečítané - + Are you sure you would like to mark all messages read? Ste si istý, že chcete označiť všetky správy ako prečítané? - + Doing work necessary to send broadcast. Vykonávam prácu potrebnú na rozoslanie. - + Proof of work pending Vykonávaná práca - + %n object(s) pending proof of work %n objekt čaká na vykonanie práce%n objekty čakajú na vykonanie práce%n objektov čaká na vykonanie práce - + %n object(s) waiting to be distributed %n objekt čaká na rozoslanie%n objekty čakajú na rozoslanie%n objektov čaká na rozoslanie - + Wait until these tasks finish? Počkať, kým tieto úlohy skončia? @@ -1363,88 +1363,143 @@ Priímcova požadovaná obtiažnosť: %1 a %2 The time on your computer, %1, may be wrong. Please verify your settings. Čas na vašom počítači, %1, možno nie je správny. Prosím, skontrolujete nastavenia. + + + The name %1 was not found. + Meno % nenájdené. + + + + The namecoin query failed (%1) + Dotaz prostredníctvom namecoinu zlyhal (%1) + + + + The namecoin query failed. + Dotaz prostredníctvom namecoinu zlyhal. + + + + The name %1 has no valid JSON data. + Meno %1 neobsahuje planté JSON dáta. + + + + The name %1 has no associated Bitmessage address. + Meno %1 nemá priradenú žiadnu adresu Bitmessage. + + + + Success! Namecoind version %1 running. + Úspech! Namecoind verzia %1 spustený. + + + + Success! NMControll is up and running. + Úspech! NMControl spustený. + + + + Couldn't understand NMControl. + Nie je rozumieť NMControl-u. + Your GPU(s) did not calculate correctly, disabling OpenCL. Please report to the developers. Vaša grafická karta vykonala nesprávny výpočet, deaktivujem OpenCL. Prosím, pošlite správu vývojárom. - + + + Welcome to easy and secure Bitmessage + * send messages to other people + * send broadcast messages like twitter or + * discuss in chan(nel)s with other people + + +Vitajte v jednoduchom a bezpečnom Bitmessage +* posielajte správy druhým ľuďom +* posielajte hromadné správy ako na twitteri alebo +* diskutuje s druhými v kanáloch + + + + not recommended for chans nie je odporúčaná pre kanály - + Problems connecting? Try enabling UPnP in the Network Settings Problémy so spojením? Skúste zapnúť UPnP v Nastaveniach siete - + Error: Bitmessage addresses start with BM- Please check the recipient address %1 Chyba: Bitmessage adresy začínajú s BM- Prosím skontrolujte adresu príjemcu %1 - + Error: The recipient address %1 is not typed or copied correctly. Please check it. Chyba: adresa príjemcu %1 nie je na správne napísaná alebo skopírovaná. Prosím skontrolujte ju. - + Error: The recipient address %1 contains invalid characters. Please check it. Chyba: adresa príjemcu %1 obsahuje neplatné znaky. Prosím skontrolujte ju. - + Error: The version of the recipient address %1 is too high. Either you need to upgrade your Bitmessage software or your acquaintance is being clever. Chyba: verzia adresy príjemcu %1 je príliš veľká. Buď musíte aktualizovať program Bitmessage alebo váš známy s vami žartuje. - + Error: Some data encoded in the recipient address %1 is too short. There might be something wrong with the software of your acquaintance. Chyba: niektoré údaje zakódované v adrese príjemcu %1 sú príliš krátke. Softér vášho známeho možno nefunguje správne. - + Error: Some data encoded in the recipient address %1 is too long. There might be something wrong with the software of your acquaintance. Chyba: niektoré údaje zakódované v adrese príjemcu %1 sú príliš dlhé. Softvér vášho známeho možno nefunguje správne. - + Error: Some data encoded in the recipient address %1 is malformed. There might be something wrong with the software of your acquaintance. Chyba: niektoré údaje zakódované v adrese príjemcu %1 sú poškodené. Softvér vášho známeho možno nefunguje správne. - + Error: Something is wrong with the recipient address %1. Chyba: niečo s adresou príjemcu %1 je nie je v poriadku. - + Synchronisation pending Prebieha synchronizácia - + Bitmessage hasn't synchronised with the network, %n object(s) to be downloaded. If you quit now, it may cause delivery delays. Wait until the synchronisation finishes? Bitmessage sa nezosynchronizoval so sieťou, treba prevzať ešte %n objekt. Keď program ukončíte teraz, môže dôjsť k spozdeniu doručenia. Počkať, kým sa synchronizácia ukončí?Bitmessage sa nezosynchronizoval so sieťou, treba prevzať ešte %n objekty. Keď program ukončíte teraz, môže dôjsť k spozdeniu doručenia. Počkať, kým sa synchronizácia ukončí?Bitmessage sa nezosynchronizoval so sieťou, treba prevzať ešte %n objektov. Keď program ukončíte teraz, môže dôjsť k spozdeniu doručenia. Počkať, kým sa synchronizácia ukončí? - + Not connected Nepripojený - + Bitmessage isn't connected to the network. If you quit now, it may cause delivery delays. Wait until connected and the synchronisation finishes? Bitmessage nie je pripojený do siete. Keď program ukončíte teraz, môže dôjsť k spozdeniu doručenia. Počkať, kým dôjde k spojeniu a prebehne synchronizácia? - + Waiting for network connection... Čakám na pripojenie do siete... - + Waiting for finishing synchronisation... Čakám na ukončenie synchronizácie... @@ -2005,6 +2060,14 @@ Predvoľba je pomocou generátora náhodných čísiel, ale deterministické adr C PoW modul nie je dostupný. Prosím, zostavte ho. + + qrcodeDialog + + + QR-code + QR kód + + regenerateAddressesDialog From 964809bbd6a3896f343696dcd99a4d8a2cb1e3e9 Mon Sep 17 00:00:00 2001 From: PyBitmessage Translations Date: Tue, 7 Mar 2017 10:23:54 +0100 Subject: [PATCH 014/407] Auto-updated language ru from transifex --- src/translations/bitmessage_ru.qm | Bin 91853 -> 91913 bytes src/translations/bitmessage_ru.ts | 312 +++++++++++++++--------------- 2 files changed, 160 insertions(+), 152 deletions(-) diff --git a/src/translations/bitmessage_ru.qm b/src/translations/bitmessage_ru.qm index 619e21fdf1089434e73f7438017fcfb0f55541d5..f6e79656161ac4b6782468ae110b55ca9d5fa6b8 100644 GIT binary patch delta 682 zcmW+!T}V>_6h7zvdhe#9WRl@-lD4GWFx{rBP4ZvK+0;2FibPIRQu8mcNCWA|Kx&+a z%uF9b(qaXHXoW9D5yX6`3?v3&j6lec9G0YZ>*4Ty95~;1INu2b_@M><)-LH60Hg!z z-un^_j3?e#R+vzl0Q}q#VEIcBrddEw4v5tSDo*+QMflqncH<_MVSLgSxLYm&=C?u3 zj2*zd7izn$fG3lnPPn1YuhA8>;3t|#1$!un21a2ik}+a0*%$?vsGcducJea=s$d%1 zfKxQg#^Y0(WQ(LZ*5=p;=yXz;&B0bOapkG^x!n&v02_UrZ8jg!u5fvdX@IYfxZ=P% zK;sbSePU1-RW-Q=)iRPFNOJIlm%V_6P5iLc05E3bN3v4^J_0}bvl}ougZ#W6muZn# zq&Gt4WLQ1MIE8s7=B{x4d_O?lfG}1X0(6HcMX*Q{LasA2GujJ8he*hK^%a84w>X9>umn@h~HmNzrt#Lfx2AFEoICrK2b9-n; zQgDP8B}H;+mikr!OH8z-_Fhw>Y>_&(=SuDY9)CvlvW{`GbV<&^6`GgzQkM4f#Sdy` zmGoV1V$5;m(zvmU{2C+fP*Bs6x)AllnGBe@6Sb|20!(}nZA?i3kj&9X+IsHw_<>eW^=2V*IqHH$_17Bz;@?O_&62dg4yt0J*RXViidDj_7>3{PY-jVFof$?nGTcd)iIfw=ZK-VYZ~mb!iJZoU1pXFcpad3aU{)(k z*B4Y`7J-n3mKSCOUK)j{7v)8WQOPQbtWXi8vO`x7eDFTs3vYN%oShZ>cSt`0q=T$O zkp#O?y&Neoj4$~F@mUz;zXrpI4pd`;vC5~hJu+HE(~E^D9Pxy@ zKcX7fXt;d369{%021}!$z&NKGT+&IyQkCY;Ti_OhD{h~$`>q$r`)IsawhL68#1n=F zp=^~CjP~um&6q>KR)D^>m?NH}Am3gN881k(cAw?wph6{ z_!Z>rlLtQk2IdaPW1dY={uY}h7m4hX8sluHisyfU2Q8+`tr4wD4o^uc)$x*~N{Obo z9V?&`Vo_Ep!b!3##jTtwxdY1WQQ8-?0o!qx#5im9iHbN*-KA( zT27Yq%HrAA+D+wkxj}Gz;E*Xm9-c6z(F{jTt EmailGatewayRegistrationDialog - + Registration failed: Регистрация не удалась: - + The requested email address is not available, please try a new one. Fill out the new desired email address (including @mailchuck.com) below: Запрашиваемый адрес email недоступен, попробуйте ввести другой. Введите желаемый адрес (включая @mailchuck.com) ниже: @@ -168,52 +168,52 @@ Please type the desired email address (including @mailchuck.com) below: MainWindow - + Reply to sender Ответить отправителю - + Reply to channel Ответить в канал - + Add sender to your Address Book Добавить отправителя в адресную книгу - + Add sender to your Blacklist Добавить отправителя в чёрный список - + Move to Trash Поместить в корзину - + Undelete Отменить удаление - + View HTML code as formatted text Просмотреть HTML код как отформатированный текст - + Save message as... Сохранить сообщение как ... - + Mark Unread Отметить как непрочитанное - + New Новый адрес @@ -238,12 +238,12 @@ Please type the desired email address (including @mailchuck.com) below: Скопировать адрес в буфер обмена - + Special address behavior... Особое поведение адресов... - + Email gateway Email-шлюз @@ -253,37 +253,37 @@ Please type the desired email address (including @mailchuck.com) below: Удалить - + Send message to this address Отправить сообщение на этот адрес - + Subscribe to this address Подписаться на рассылку с этого адреса - + Add New Address Добавить новый адрес - + Copy destination address to clipboard Скопировать адрес отправки в буфер обмена - + Force send Форсировать отправку - + One of your addresses, %1, is an old version 1 address. Version 1 addresses are no longer supported. May we delete it now? Один из Ваших адресов, %1, является устаревшим адресом версии 1. Адреса версии 1 больше не поддерживаются. Хотите ли Вы удалить его сейчас? - + Waiting for their encryption key. Will request it again soon. Ожидаем ключ шифрования от Вашего собеседника. Запрос будет повторен через некоторое время. @@ -293,17 +293,17 @@ Please type the desired email address (including @mailchuck.com) below: - + Queued. В очереди. - + Message sent. Waiting for acknowledgement. Sent at %1 Сообщение отправлено. Ожидаем подтверждения. Отправлено в %1 - + Message sent. Sent at %1 Сообщение отправлено в %1 @@ -313,47 +313,47 @@ Please type the desired email address (including @mailchuck.com) below: - + Acknowledgement of the message received %1 Сообщение доставлено в %1 - + Broadcast queued. Рассылка ожидает очереди. - + Broadcast on %1 Рассылка на %1 - + Problem: The work demanded by the recipient is more difficult than you are willing to do. %1 Проблема: Ваш получатель требует более сложных вычислений, чем максимум, указанный в Ваших настройках. %1 - + Problem: The recipient's encryption key is no good. Could not encrypt message. %1 Проблема: ключ получателя неправильный. Невозможно зашифровать сообщение. %1 - + Forced difficulty override. Send should start soon. Форсирована смена сложности. Отправляем через некоторое время. - + Unknown status: %1 %2 Неизвестный статус: %1 %2 - + Not Connected Не соединено - + Show Bitmessage Показать Bitmessage @@ -363,12 +363,12 @@ Please type the desired email address (including @mailchuck.com) below: Отправить - + Subscribe Подписки - + Channel Канал @@ -378,13 +378,13 @@ Please type the desired email address (including @mailchuck.com) below: Выйти - + You may manage your keys by editing the keys.dat file stored in the same directory as this program. It is important that you back up this file. Вы можете управлять Вашими ключами, отредактировав файл keys.dat, находящийся в той же папке, что и эта программа. Создайте резервную копию этого файла перед тем как будете его редактировать. - + You may manage your keys by editing the keys.dat file stored in %1 It is important that you back up this file. @@ -393,19 +393,19 @@ It is important that you back up this file. Создайте резервную копию этого файла перед тем как будете его редактировать. - + Open keys.dat? Открыть файл keys.dat? - + You may manage your keys by editing the keys.dat file stored in the same directory as this program. It is important that you back up this file. Would you like to open the file now? (Be sure to close Bitmessage before making any changes.) Вы можете управлять Вашими ключами, отредактировав файл keys.dat, находящийся в той же папке, что и эта программа. Создайте резервную копию этого файла перед тем как будете его редактировать. Хотели бы Вы открыть этот файл сейчас? (пожалуйста, закройте Bitmessage до того как Вы внесёте в этот файл какие-либо изменения.) - + You may manage your keys by editing the keys.dat file stored in %1 It is important that you back up this file. Would you like to open the file now? (Be sure to close Bitmessage before making any changes.) @@ -415,37 +415,37 @@ It is important that you back up this file. Would you like to open the file now? (пожалуйста, закройте Bitmessage до того как Вы внесёте в этот файл какие-либо изменения.) - + Delete trash? Очистить корзину? - + Are you sure you want to delete all trashed messages? Вы уверены что хотите очистить корзину? - + bad passphrase Неподходящая секретная фраза - + You must type your passphrase. If you don't have one then this is not the form for you. Вы должны ввести секретную фразу. Если Вы не хотите этого делать, то Вы выбрали неправильную опцию. - + Bad address version number Неверный номер версии адреса - + Your address version number must be a number: either 3 or 4. Адрес номера версии должен быть числом: либо 3, либо 4. - + Your address version number must be either 3 or 4. Адрес номера версии должен быть либо 3, либо 4. @@ -515,22 +515,22 @@ It is important that you back up this file. Would you like to open the file now? - + Connection lost Соединение потеряно - + Connected Соединено - + Message trashed Сообщение удалено - + The TTL, or Time-To-Live is the length of time that the network will hold the message. The recipient must get it during this time. If your Bitmessage client does not hear an acknowledgement, it will resend the message automatically. The longer the Time-To-Live, the @@ -541,17 +541,17 @@ It is important that you back up this file. Would you like to open the file now? сообщение. Часто разумным вариантом будет установка TTL на 4 или 5 дней. - + Message too long Сообщение слишком длинное - + The message that you are trying to send is too long by %1 bytes. (The maximum is 261644 bytes). Please cut it down before sending. Сообщение, которое вы пытаетесь отправить, длиннее максимально допустимого на %1 байт. (Максимально допустимое значение 261644 байта). Пожалуйста, сократите сообщение перед отправкой. - + Error: Your account wasn't registered at an email gateway. Sending registration now as %1, please wait for the registration to be processed before retrying sending. Ошибка: ваш аккаунт не зарегистрирован на Email-шлюзе. Отправка регистрации %1, пожалуйста, подождите пока процесс регистрации не завершится, прежде чем попытаться отправить сообщение заново. @@ -596,67 +596,67 @@ It is important that you back up this file. Would you like to open the file now? - + Error: You must specify a From address. If you don't have one, go to the 'Your Identities' tab. Вы должны указать адрес в поле "От кого". Вы можете найти Ваш адрес во вкладке "Ваши Адреса". - + Address version number Версия адреса - + Concerning the address %1, Bitmessage cannot understand address version numbers of %2. Perhaps upgrade Bitmessage to the latest version. По поводу адреса %1: Bitmessage не поддерживаем адреса версии %2. Возможно, Вам нужно обновить клиент Bitmessage. - + Stream number Номер потока - + Concerning the address %1, Bitmessage cannot handle stream numbers of %2. Perhaps upgrade Bitmessage to the latest version. По поводу адреса %1: Bitmessage не поддерживаем стрим номер %2. Возможно, Вам нужно обновить клиент Bitmessage. - + Warning: You are currently not connected. Bitmessage will do the work necessary to send the message but it won't send until you connect. Внимание: Вы не подключены к сети. Bitmessage выполнит работу, требуемую для отправки сообщения, но не отправит его до тех пор, пока Вы не подключитесь. - + Message queued. Сообщение в очереди. - + Your 'To' field is empty. Вы не заполнили поле 'Кому'. - + Right click one or more entries in your address book and select 'Send message to this address'. Нажмите правую кнопку мыши на каком-либо адресе и выберите "Отправить сообщение на этот адрес". - + Fetched address from namecoin identity. Получить адрес через Namecoin. - + New Message Новое сообщение - + From От - + Sending email gateway registration request Отправка запроса на регистрацию на Email-шлюзе @@ -671,142 +671,142 @@ It is important that you back up this file. Would you like to open the file now? Вы ввели неправильный адрес. Это адрес проигнорирован. - + Error: You cannot add the same address to your address book twice. Try renaming the existing one if you want. Ошибка: Вы не можете добавлять один и тот же адрес в Адресную Книгу несколько раз. Попробуйте переименовать существующий адрес. - + Error: You cannot add the same address to your subscriptions twice. Perhaps rename the existing one if you want. Ошибка: вы не можете добавить один и тот же адрес в ваши подписки дважды. Пожалуйста, переименуйте имеющийся адрес, если хотите. - + Restart Перезапустить - + You must restart Bitmessage for the port number change to take effect. Вы должны перезапустить Bitmessage, чтобы смена номера порта имела эффект. - + Bitmessage will use your proxy from now on but you may want to manually restart Bitmessage now to close existing connections (if any). Bitmessage будет использовать Ваш прокси, начиная прямо сейчас. Тем не менее Вам имеет смысл перезапустить Bitmessage, чтобы закрыть уже существующие соединения. - + Number needed Требуется число - + Your maximum download and upload rate must be numbers. Ignoring what you typed. Скорости загрузки и выгрузки должны быть числами. Игнорирую то, что вы набрали. - + Will not resend ever Не пересылать никогда - + Note that the time limit you entered is less than the amount of time Bitmessage waits for the first resend attempt therefore your messages will never be resent. Обратите внимание, что лимит времени, который вы ввели, меньше чем время, которое Bitmessage ждет перед первой попыткой переотправки сообщения, поэтому ваши сообщения никогда не будут переотправлены. - + Sending email gateway unregistration request Отправка запроса на отмену регистрации на Email-шлюзе - + Sending email gateway status request Отправка запроса статуса аккаунта на Email-шлюзе - + Passphrase mismatch Секретная фраза не подходит - + The passphrase you entered twice doesn't match. Try again. Вы ввели две разные секретные фразы. Пожалуйста, повторите заново. - + Choose a passphrase Придумайте секретную фразу - + You really do need a passphrase. Вы действительно должны ввести секретную фразу. - + Address is gone Адрес утерян - + Bitmessage cannot find your address %1. Perhaps you removed it? Bitmessage не может найти Ваш адрес %1. Возможно Вы удалили его? - + Address disabled Адрес выключен - + Error: The address from which you are trying to send is disabled. You'll have to enable it on the 'Your Identities' tab before using it. Ошибка: адрес, с которого Вы пытаетесь отправить, выключен. Вам нужно будет включить этот адрес во вкладке "Ваши адреса". - + Entry added to the Address Book. Edit the label to your liking. Запись добавлена в Адресную Книгу. Вы можете её отредактировать. - + Entry added to the blacklist. Edit the label to your liking. Запись добавлена в чёрный список. Измените название по своему вкусу. - + Error: You cannot add the same address to your blacklist twice. Try renaming the existing one if you want. Ошибка: вы не можете добавить один и тот же адрес в чёрный список дважды. Попробуйте переименовать существующий адрес. - + Moved items to trash. Удалено в корзину. - + Undeleted item. Отменить удаление записи - + Save As... Сохранить как ... - + Write error. Ошибка записи. - + No addresses selected. Вы не выбрали адрес. - + If you delete the subscription, messages that you already received will become inaccessible. Maybe you can consider disabling the subscription instead. Disabled subscriptions will not receive new messages, but you can still view messages you already received. Are you sure you want to delete the subscription? @@ -815,7 +815,7 @@ Are you sure you want to delete the subscription? Вы уверены, что хотите отменить подписку? - + If you delete the channel, messages that you already received will become inaccessible. Maybe you can consider disabling the channel instead. Disabled channels will not receive new messages, but you can still view messages you already received. Are you sure you want to delete the channel? @@ -824,92 +824,92 @@ Are you sure you want to delete the channel? Вы уверены, что хотите удалить канал? - + Do you really want to remove this avatar? Вы уверены, что хотите удалить этот аватар? - + You have already set an avatar for this address. Do you really want to overwrite it? У вас уже есть аватар для этого адреса. Вы уверены, что хотите перезаписать аватар? - + Start-on-login not yet supported on your OS. Запуск программы при входе в систему ещё не поддерживается в вашей операционной системе. - + Minimize-to-tray not yet supported on your OS. Сворачивание в трей ещё не поддерживается в вашей операционной системе. - + Tray notifications not yet supported on your OS. Уведомления в трее ещё не поддерживаеются в вашей операционной системе. - + Testing... Проверяем... - + This is a chan address. You cannot use it as a pseudo-mailing list. Это адрес chan-а. Вы не можете его использовать как адрес рассылки. - + The address should start with ''BM-'' Адрес должен начинаться с "BM-" - + The address is not typed or copied correctly (the checksum failed). Адрес введен или скопирован неверно (контрольная сумма не сходится). - + The version number of this address is higher than this software can support. Please upgrade Bitmessage. Версия этого адреса более поздняя, чем Ваша программа. Пожалуйста, обновите программу Bitmessage. - + The address contains invalid characters. Адрес содержит запрещённые символы. - + Some data encoded in the address is too short. Данные, закодированные в адресе, слишком короткие. - + Some data encoded in the address is too long. Данные, закодированные в адресе, слишком длинные. - + Some data encoded in the address is malformed. Данные, закодированные в адресе, имеют неверный формат. - + Enter an address above. Введите адрес выше. - + Address is an old type. We cannot display its past broadcasts. Адрес старого типа. Мы не можем отобразить его прошлые рассылки. - + There are no recent broadcasts from this address to display. Нет недавних рассылок с этого адреса для отображения. - + You are using TCP port %1. (This can be changed in the settings). Вы используете TCP порт %1. (Его можно поменять в настройках). @@ -1119,47 +1119,47 @@ Are you sure you want to delete the channel? Добавить новую запись - + Display the %1 recent broadcast(s) from this address. Показать %1 прошлых рассылок с этого адреса. - + New version of PyBitmessage is available: %1. Download it from https://github.com/Bitmessage/PyBitmessage/releases/latest Доступна новая версия PyBitmessage: %1. Загрузите её: https://github.com/Bitmessage/PyBitmessage/releases/latest - + Waiting for PoW to finish... %1% Ожидание окончания PoW... %1% - + Shutting down Pybitmessage... %1% Завершение PyBitmessage... %1% - + Waiting for objects to be sent... %1% Ожидание отправки объектов... %1% - + Saving settings... %1% Сохранение настроек... %1% - + Shutting down core... %1% Завершение работы ядра... %1% - + Stopping notifications... %1% Остановка сервиса уведомлений... %1% - + Shutdown imminent... %1% Завершение вот-вот произойдет... %1% @@ -1169,17 +1169,17 @@ Are you sure you want to delete the channel? %n час%n часа%n часов%n час(а/ов) - + %n day(s) %n день%n дня%n дней%n дней - + Shutting down PyBitmessage... %1% Завершение PyBitmessage... %1% - + Sent Отправленные @@ -1283,7 +1283,7 @@ Receiver's required difficulty: %1 and %2 Проблема: вы пытаетесь отправить сообщение самому себе или в чан, но ваш ключ шифрования не найден в файле ключей keys.dat. Невозможно зашифровать сообщение. %1 - + Doing work necessary to send message. Выполнение работы, требуемой для отправки сообщения. @@ -1293,7 +1293,7 @@ Receiver's required difficulty: %1 and %2 Сообщение отправлено. Ожидаем подтверждения. Отправлено на %1 - + Doing work necessary to request encryption key. Выполнение работы, требуемой для запроса ключа шифрования. @@ -1318,37 +1318,37 @@ Receiver's required difficulty: %1 and %2 Распределение портов UPnP отменено - + Mark all messages as read Отметить все сообщения как прочтенные - + Are you sure you would like to mark all messages read? Вы уверены, что хотите отметить все сообщения как прочтенные? - + Doing work necessary to send broadcast. Выполнение работы, требуемой для отправки рассылки. - + Proof of work pending Ожидается доказательство работы - + %n object(s) pending proof of work %n объект в ожидании доказательства работы%n объекта в ожидании доказательства работы%n объектов в ожидании доказательства работы%n объектов в ожидании доказательства работы - + %n object(s) waiting to be distributed %n объект ожидает раздачи%n объекта ожидают раздачи%n объектов ожидают раздачи%n объектов ожидают раздачи - + Wait until these tasks finish? Подождать завершения этих задач? @@ -1413,7 +1413,7 @@ Receiver's required difficulty: %1 and %2 Ваша видеокарта вычислила неправильно, отключаем OpenCL. Пожалуйста, сообщите разработчикам. - + Welcome to easy and secure Bitmessage * send messages to other people @@ -1427,82 +1427,82 @@ Receiver's required difficulty: %1 and %2 * участвуйте в обсуждениях в чанах - + not recommended for chans не рекомендовано для чанов - + Problems connecting? Try enabling UPnP in the Network Settings Проблемы подключения? Попробуйте включить UPnP в сетевых настройках. - + Error: Bitmessage addresses start with BM- Please check the recipient address %1 Ошибка: адреса Bitmessage начинаются с "BM-". Пожалуйста, проверьте адрес получателя %1. - + Error: The recipient address %1 is not typed or copied correctly. Please check it. Ошибка: адрес получателя %1 набран или скопирован неправильно. Пожалуйста, проверьте его. - + Error: The recipient address %1 contains invalid characters. Please check it. Ошибка: адрес получателя %1 содержит недопустимые символы. Пожалуйста, проверьте его. - + Error: The version of the recipient address %1 is too high. Either you need to upgrade your Bitmessage software or your acquaintance is being clever. Ошибка: версия адреса получателя %1 слишком высокая. Либо вам нужно обновить программу Bitmessage, либо ваш знакомый - умник. - + Error: Some data encoded in the recipient address %1 is too short. There might be something wrong with the software of your acquaintance. Ошибка: часть данных, закодированных в адресе получателя %1 слишком короткая. Видимо, что-то не так с программой, используемой вашим знакомым. - + Error: Some data encoded in the recipient address %1 is too long. There might be something wrong with the software of your acquaintance. Ошибка: часть данных, закодированных в адресе получателя %1 слишком длинная. Видимо, что-то не так с программой, используемой вашим знакомым. - + Error: Some data encoded in the recipient address %1 is malformed. There might be something wrong with the software of your acquaintance. Ошибка: часть данных, закодированных в адресе получателя %1 сформирована неправильно. Видимо, что-то не так с программой, используемой вашим знакомым. - + Error: Something is wrong with the recipient address %1. Ошибка: что-то не так с адресом получателя %1. - + Synchronisation pending Ожидается синхронизация - + Bitmessage hasn't synchronised with the network, %n object(s) to be downloaded. If you quit now, it may cause delivery delays. Wait until the synchronisation finishes? Bitmessage не синхронизирован с сетью, незагруженных объектов: %n. Выход сейчас может привести к задержкам доставки. Подождать завершения синхронизации?Bitmessage не синхронизирован с сетью, незагруженных объектов: %n. Выход сейчас может привести к задержкам доставки. Подождать завершения синхронизации?Bitmessage не синхронизирован с сетью, незагруженных объектов: %n. Выход сейчас может привести к задержкам доставки. Подождать завершения синхронизации?Bitmessage не синхронизирован с сетью, незагруженных объектов: %n. Выход сейчас может привести к задержкам доставки. Подождать завершения синхронизации? - + Not connected Не подключено - + Bitmessage isn't connected to the network. If you quit now, it may cause delivery delays. Wait until connected and the synchronisation finishes? Bitmessage не подключен к сети. Выход сейчас может привести к задержкам доставки. Подождать подключения и завершения синхронизации? - + Waiting for network connection... Ожидание сетевого подключения... - + Waiting for finishing synchronisation... Ожидание окончания синхронизации... @@ -2062,6 +2062,14 @@ The 'Random Number' option is selected by default but deterministic ad Модуль C для PoW недоступен. Пожалуйста, соберите его. + + qrcodeDialog + + + QR-code + QR-код + + regenerateAddressesDialog From b28fe3a220a7ac1b8dd5a9c9e0baf54b9c6ce723 Mon Sep 17 00:00:00 2001 From: PyBitmessage Translations Date: Tue, 7 Mar 2017 12:08:19 +0100 Subject: [PATCH 015/407] Auto-updated language fr from transifex --- src/translations/bitmessage_fr.qm | Bin 95018 -> 97042 bytes src/translations/bitmessage_fr.ts | 365 ++++++++++++++++++------------ 2 files changed, 214 insertions(+), 151 deletions(-) diff --git a/src/translations/bitmessage_fr.qm b/src/translations/bitmessage_fr.qm index 9e97aa12752666d4ea9e40057829d3cf7ffa880f..2eccb916105d2b8c6b91deea59a3f929ba821f02 100644 GIT binary patch delta 4894 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 CGh4O* delta 3315 zcmX9=c|c8h8-9NG+;i_e=bn47vSj&&s1TJz+Cyb6H$;gfOD0i?nHJG4S+i7@B#Fq3 zHA^)`CJckIBqCFo=^MLA2w6Vrd(~ghJ?GqWe((D}&-=XZrY8R26F$9zVLyO@V3t}y zo&n4&0QjRo@jzg-6<{p^lP&?vN`Yzp=>Ak7%nK|u5AsS2Ap8~NHPe9w-jLVZ0udKm zktq&=UoQtTW`ocA4D2fepW_8=;|V7LIR)Sk=Ytviv%r_G1)FvmiUS;d_$rkCBf#d` zLKtz94t@dco(f>pCK!!)3)E-8==dhE&PsHR-v}H_f>~8F5PAT;RqjBcDf)M%{~x*r zt1I7ujpH%!B3-YV0K1G_;Pg#6RHp!Mg5hG)00X}7f)V*EfY?Ti%DezrIJM!I(-`y2 z4{&*oaa&(ghKn#RQzXoRZ}<|h5e1kScbclVrw!lQ!ap?}Y`lPgdGvhzR!k|TfESAZGAa4XIQVC5kLs4Pvp%r1~zR)erbCO$Q*|^OafzxIO0an7dk;3jT(-cv7|87=i&zs0IHk!9%c20= zkFwNiBe32+vh}UccgZ#^Yyi%!lkEz51oZWi{S-w14<8^a)KY*S_R6k@5N_@-tBx=S z5)HDu%g%%O7RhS0Gr>Cf$)3m;18<`ZoV1=NQPgponYk%S)RbmKd znnNNwGl(;D+)JvDYV|J((LmqXxKa z!i|ZR110UaurE~l$6OQhl|TFX+1jP@Is*ekE=n zHp-LV-vP?J?+i!&dv~da}GQrvp_cN`C5~ zJ6N9|<>%@x>EKNHg?J0VXTQAK_&0j4lh>YkOGf3*vwJqw|E?=}hu2xa(nq}0mDgZe zYu-7GM3}#g_jpGcyL!mvNq-Yk!U?XyK!BsiYoj_$0du zFee3{k`xLyv4GEVvIJ_L^Vx3~gW25WcdVgvGAsFlwLIY3g)f@SQAQDO_*3880C{)# zignbV-Xi~ElrLZt$~SS8dF*cfgO?E*Q-s1OoId35p$J)&3#PPFM9d{Biq|Nj?${Ak zu8JSisf#8)RIG_1YC_g3a>hOen=@09fBy!sGE1?4Gf~p3NO7b;B{p@bqHv?3J@9J0 zqU0ObU*0G#PuBvTjf&d`jmg3OP(1J_j(jzW1_d2dAX6HrQ@|{M>F{|lJtqD=2ZV%B9U z(+!U)68!_^)~Z|};H)xxegQDcO1UeKG8kE_+!McoL^MfxXgzfU!rHL!jW%qIX~X88 z$`WsS|0fgWZ}wD~z@y4a|6o8kqWt>~`Nqke%BT8o6gDb9Sd;@5CMrk88>&={%Ha5> z0m#~|@^ha|{hzH0EZ+iHj#tHwupvz-RjGSv;ys$K%IHVxtua+?KC1;PndU5bqX`zqm%Ki&v#cxzs4c*C@o~mkBMFP9hRJGgbI{3Zn(Su-M_7m01&$+<3gf?s} zHmI7GM*|HXgbq*qz>Inb-F_w`nrI~SzWRn7N-Nl9#Zy;w6&!+Q12G*0$9B$uct-F+ z3vf0@2y79l{}YA9dJ15^sgV4sncC1%SU-jic8(M_x03_gBZVz52LWR*3EAH&l0RL@ z`NbK`#BfO1neqp4$4J=iN+0~XQaGMR;)}EruG@75Gy9)#{VSE!48o0v)Mk5KgCo-r?Q5{XH$6sRmaf#%Zuf+DD6=dbnVlVcHDl-#R8ot2F|^(XOn*ZRn?ncsUlPOXDAM|ot;npK#Ka&PA|4aO z?5A|#?H*!*`)3mAII%FGnF9JlJgI(10e%$ER#0Z0j)~`!s4e#~v2qoOa1j}t~s z)gr#!MuB-eRI_mf)c^bH)IvO|-!o1vayNnXrfTug4`AlSYE3dNB4!cl&e_DJ($;NO^5WY*uQC$t=(#HOfc zCy-`LJk;x!q!5bg^fR>v;-arQyULRcVyrqx7))Z?qQ0Co4><3tzWh%CZ8FyCyW90( z1D(`0@eN>;cc^PeZw2}lsq6A+?rZDR^~Qa`WKY!frsN;PXG*r;)SuT*8fHZmeAz?t zDkeiqEtIA#n-BCUlIFahL4NU33e7a+0<|U5{39Mz5+*Givy)b)Jn4sfE`XV@6dy=d z`%AR6?)P-CcB#^qp`?lFF4C4+V}a+<(t)_Qv?o+bCyePkJ?}~<+=$AOGU>#(E;ybe zl}3?hU;R%iV~fc-2ecyd8!Vm491d)lCsi1pCITapq{?tTO}+clOnN{**?%NxkkfffsZH zHqVqQlKo73Er;4rQLU{Q(1qF%ZP;gG8#W4U`1P!|Mo$~jOT+5cA8f!J?W+mqv_P!Y zHh0&8_3fh@V9}L~X0*;O@<&=eH|y*#cc&%uADv6LEnpVQb>6>FrIxwseDkTaS3Px8 zkClT7zPga?6=bzJy2vyq8a6GuXe-LxI6;>@lHTvqL6=s099TV5m*2gH*7}&=bqB9E zgW1*U3Px#xPm#LP9=YUXp}Mjk8fgC4=zi-+c(hn|D^gE${GRTy8})J4VBO~pE5Q5~ t)1;X@bF;03e_@zrMbE<0fqK)zbrXBcE)+`^iG|h|0^>_gyD@*a{{u@^;GqBj diff --git a/src/translations/bitmessage_fr.ts b/src/translations/bitmessage_fr.ts index 0a041f2d..43d46f40 100644 --- a/src/translations/bitmessage_fr.ts +++ b/src/translations/bitmessage_fr.ts @@ -58,12 +58,12 @@ EmailGatewayRegistrationDialog - + Registration failed: L’inscription a échoué : - + The requested email address is not available, please try a new one. Fill out the new desired email address (including @mailchuck.com) below: L’adresse de courriel demandée n’est pas disponible, veuillez en essayer une nouvelle. Saisissez ci-dessous la nouvelle adresse de courriel désirée (incluant @mailchuck.com) : @@ -173,52 +173,52 @@ Veuillez taper l’adresse de courriel désirée (incluant @mailchuck.com) : MainWindow - + Reply to sender Répondre à l’expéditeur - + Reply to channel Répondre au canal - + Add sender to your Address Book Ajouter l’expéditeur au carnet d’adresses - + Add sender to your Blacklist Ajouter l’expéditeur à votre liste noire - + Move to Trash Envoyer à la Corbeille - + Undelete Restaurer - + View HTML code as formatted text Voir le code HTML comme du texte formaté - + Save message as... Enregistrer le message sous… - + Mark Unread Marquer comme non-lu - + New Nouvelle @@ -243,12 +243,12 @@ Veuillez taper l’adresse de courriel désirée (incluant @mailchuck.com) :Copier l’adresse dans le presse-papier - + Special address behavior... Comportement spécial de l’adresse… - + Email gateway Passerelle de courriel @@ -258,37 +258,37 @@ Veuillez taper l’adresse de courriel désirée (incluant @mailchuck.com) :Effacer - + Send message to this address Envoyer un message à cette adresse - + Subscribe to this address S’abonner à cette adresse - + Add New Address Ajouter une nouvelle adresse - + Copy destination address to clipboard Copier l’adresse de destination dans le presse-papier - + Force send Forcer l’envoi - + One of your addresses, %1, is an old version 1 address. Version 1 addresses are no longer supported. May we delete it now? Une de vos adresses, %1, est une vieille adresse de la version 1. Les adresses de la version 1 ne sont plus supportées. Nous pourrions la supprimer maintenant? - + Waiting for their encryption key. Will request it again soon. En attente de la clé de chiffrement. Une nouvelle requête sera bientôt lancée. @@ -298,17 +298,17 @@ Veuillez taper l’adresse de courriel désirée (incluant @mailchuck.com) : - + Queued. En attente. - + Message sent. Waiting for acknowledgement. Sent at %1 Message envoyé. En attente de l’accusé de réception. Envoyé %1 - + Message sent. Sent at %1 Message envoyé. Envoyé %1 @@ -318,47 +318,47 @@ Veuillez taper l’adresse de courriel désirée (incluant @mailchuck.com) : - + Acknowledgement of the message received %1 Accusé de réception reçu %1 - + Broadcast queued. Message de diffusion en attente. - + Broadcast on %1 Message de diffusion du %1 - + Problem: The work demanded by the recipient is more difficult than you are willing to do. %1 Problème : Le travail demandé par le destinataire est plus difficile que ce que vous avez paramétré. %1 - + Problem: The recipient's encryption key is no good. Could not encrypt message. %1 Problème : la clé de chiffrement du destinataire n’est pas bonne. Il n’a pas été possible de chiffrer le message. %1 - + Forced difficulty override. Send should start soon. Neutralisation forcée de la difficulté. L’envoi devrait bientôt commencer. - + Unknown status: %1 %2 Statut inconnu : %1 %2 - + Not Connected Déconnecté - + Show Bitmessage Afficher Bitmessage @@ -368,12 +368,12 @@ Veuillez taper l’adresse de courriel désirée (incluant @mailchuck.com) :Envoyer - + Subscribe S’abonner - + Channel Canal @@ -383,12 +383,12 @@ Veuillez taper l’adresse de courriel désirée (incluant @mailchuck.com) :Quitter - + You may manage your keys by editing the keys.dat file stored in the same directory as this program. It is important that you back up this file. Vous pouvez éditer vos clés en éditant le fichier keys.dat stocké dans le même répertoire que ce programme. Il est important de faire des sauvegardes de ce fichier. - + You may manage your keys by editing the keys.dat file stored in %1 It is important that you back up this file. @@ -396,54 +396,54 @@ It is important that you back up this file. Il est important de faire des sauvegardes de ce fichier. - + Open keys.dat? Ouvrir keys.dat ? - + You may manage your keys by editing the keys.dat file stored in the same directory as this program. It is important that you back up this file. Would you like to open the file now? (Be sure to close Bitmessage before making any changes.) Vous pouvez éditer vos clés en éditant le fichier keys.dat stocké dans le même répertoire que ce programme. Il est important de faire des sauvegardes de ce fichier. Souhaitez-vous l’ouvrir maintenant ? (Assurez-vous de fermer Bitmessage avant d’effectuer des changements.) - + You may manage your keys by editing the keys.dat file stored in %1 It is important that you back up this file. Would you like to open the file now? (Be sure to close Bitmessage before making any changes.) Vous pouvez éditer vos clés en éditant le fichier keys.dat stocké dans le répertoire %1. Il est important de faire des sauvegardes de ce fichier. Souhaitez-vous l’ouvrir maintenant? (Assurez-vous de fermer Bitmessage avant d’effectuer des changements.) - + Delete trash? Supprimer la corbeille ? - + Are you sure you want to delete all trashed messages? Êtes-vous sûr de vouloir supprimer tous les messages dans la corbeille ? - + bad passphrase Mauvaise phrase secrète - + You must type your passphrase. If you don't have one then this is not the form for you. Vous devez taper votre phrase secrète. Si vous n’en avez pas, ce formulaire n’est pas pour vous. - + Bad address version number Mauvais numéro de version d’adresse - + Your address version number must be a number: either 3 or 4. Votre numéro de version d’adresse doit être un nombre : soit 3 soit 4. - + Your address version number must be either 3 or 4. Votre numéro de version d’adresse doit être soit 3 soit 4. @@ -513,22 +513,22 @@ It is important that you back up this file. Would you like to open the file now? - + Connection lost Connexion perdue - + Connected Connecté - + Message trashed Message envoyé à la corbeille - + The TTL, or Time-To-Live is the length of time that the network will hold the message. The recipient must get it during this time. If your Bitmessage client does not hear an acknowledgement, it will resend the message automatically. The longer the Time-To-Live, the @@ -537,17 +537,17 @@ It is important that you back up this file. Would you like to open the file now? Le destinataire doit l’obtenir avant ce temps. Si votre client Bitmessage ne reçoit pas de confirmation de réception, il va le ré-envoyer automatiquement. Plus le Time-To-Live est long, plus grand est le travail que votre ordinateur doit effectuer pour envoyer le message. Un Time-To-Live de quatre ou cinq jours est souvent approprié. - + Message too long Message trop long - + The message that you are trying to send is too long by %1 bytes. (The maximum is 261644 bytes). Please cut it down before sending. Le message que vous essayez d’envoyer est trop long de %1 octets (le maximum est 261644 octets). Veuillez le réduire avant de l’envoyer. - + Error: Your account wasn't registered at an email gateway. Sending registration now as %1, please wait for the registration to be processed before retrying sending. Erreur : votre compte n’a pas été inscrit à une passerelle de courrier électronique. Envoi de l’inscription maintenant en tant que %1, veuillez patienter tandis que l’inscription est en cours de traitement, avant de retenter l’envoi. @@ -592,67 +592,67 @@ Le destinataire doit l’obtenir avant ce temps. Si votre client Bitmessage ne r - + Error: You must specify a From address. If you don't have one, go to the 'Your Identities' tab. Erreur : Vous devez spécifier une adresse d’expéditeur. Si vous n’en avez pas, rendez-vous dans l’onglet 'Vos identités'. - + Address version number Numéro de version de l’adresse - + Concerning the address %1, Bitmessage cannot understand address version numbers of %2. Perhaps upgrade Bitmessage to the latest version. Concernant l’adresse %1, Bitmessage ne peut pas comprendre les numéros de version de %2. Essayez de mettre à jour Bitmessage vers la dernière version. - + Stream number Numéro de flux - + Concerning the address %1, Bitmessage cannot handle stream numbers of %2. Perhaps upgrade Bitmessage to the latest version. Concernant l’adresse %1, Bitmessage ne peut pas supporter les nombres de flux de %2. Essayez de mettre à jour Bitmessage vers la dernière version. - + Warning: You are currently not connected. Bitmessage will do the work necessary to send the message but it won't send until you connect. Avertissement : Vous êtes actuellement déconnecté. Bitmessage fera le travail nécessaire pour envoyer le message mais il ne sera pas envoyé tant que vous ne vous connecterez pas. - + Message queued. Message mis en file d’attente. - + Your 'To' field is empty. Votre champ 'Vers' est vide. - + Right click one or more entries in your address book and select 'Send message to this address'. Cliquez droit sur une ou plusieurs entrées dans votre carnet d’adresses et sélectionnez 'Envoyer un message à ces adresses'. - + Fetched address from namecoin identity. Récupération avec succès de l’adresse de l’identité Namecoin. - + New Message Nouveau message - + From De - + Sending email gateway registration request Envoi de la demande d’inscription de la passerelle de courriel @@ -667,142 +667,142 @@ Le destinataire doit l’obtenir avant ce temps. Si votre client Bitmessage ne r L’adresse que vous avez entrée est invalide. Adresse ignorée. - + Error: You cannot add the same address to your address book twice. Try renaming the existing one if you want. Erreur : Vous ne pouvez pas ajouter une adresse déjà présente dans votre carnet d’adresses. Essayez de renommer l’adresse existante. - + Error: You cannot add the same address to your subscriptions twice. Perhaps rename the existing one if you want. Erreur : vous ne pouvez pas ajouter la même adresse deux fois à vos abonnements. Peut-être que vous pouvez renommer celle qui existe si vous le souhaitez. - + Restart Redémarrer - + You must restart Bitmessage for the port number change to take effect. Vous devez redémarrer Bitmessage pour que le changement de port prenne effet. - + Bitmessage will use your proxy from now on but you may want to manually restart Bitmessage now to close existing connections (if any). Bitmessage utilisera votre proxy dorénavant, mais vous pouvez redémarrer manuellement Bitmessage maintenant afin de fermer des connexions existantes (si il y en existe). - + Number needed Nombre requis - + Your maximum download and upload rate must be numbers. Ignoring what you typed. Vos taux maximum de téléchargement et de téléversement doivent être des nombres. Ce que vous avez tapé est ignoré. - + Will not resend ever Ne renverra jamais - + Note that the time limit you entered is less than the amount of time Bitmessage waits for the first resend attempt therefore your messages will never be resent. Notez que la limite de temps que vous avez entrée est plus courte que le temps d’attente respecté par Bitmessage avant le premier essai de renvoi, par conséquent votre message ne sera jamais renvoyé. - + Sending email gateway unregistration request Envoi de la demande de désinscription de la passerelle de courriel - + Sending email gateway status request Envoi à la passerelle de courriel d’une demande de statut - + Passphrase mismatch Phrases secrètes différentes - + The passphrase you entered twice doesn't match. Try again. Les phrases secrètes entrées sont différentes. Réessayez. - + Choose a passphrase Choisissez une phrase secrète - + You really do need a passphrase. Vous devez vraiment utiliser une phrase secrète. - + Address is gone L’adresse a disparu - + Bitmessage cannot find your address %1. Perhaps you removed it? Bitmessage ne peut pas trouver votre adresse %1. Peut-être l’avez-vous supprimée? - + Address disabled Adresse désactivée - + Error: The address from which you are trying to send is disabled. You'll have to enable it on the 'Your Identities' tab before using it. Erreur : L’adresse avec laquelle vous essayez de communiquer est désactivée. Vous devez d’abord l’activer dans l’onglet 'Vos identités' avant de l’utiliser. - + Entry added to the Address Book. Edit the label to your liking. Entrée ajoutée au carnet d’adresse. Éditez l’étiquette à votre convenance. - + Entry added to the blacklist. Edit the label to your liking. Entrée ajoutée à la liste noire. Éditez l’étiquette à votre convenance. - + Error: You cannot add the same address to your blacklist twice. Try renaming the existing one if you want. Erreur : vous ne pouvez pas ajouter la même adresse deux fois à votre liste noire. Essayez de renommer celle qui existe si vous le souhaitez. - + Moved items to trash. Messages déplacés dans la corbeille. - + Undeleted item. Articles restaurés. - + Save As... Enregistrer sous… - + Write error. Erreur d’écriture. - + No addresses selected. Aucune adresse sélectionnée. - + If you delete the subscription, messages that you already received will become inaccessible. Maybe you can consider disabling the subscription instead. Disabled subscriptions will not receive new messages, but you can still view messages you already received. Are you sure you want to delete the subscription? @@ -811,7 +811,7 @@ Are you sure you want to delete the subscription? Êtes-vous sur de vouloir supprimer cet abonnement ? - + If you delete the channel, messages that you already received will become inaccessible. Maybe you can consider disabling the channel instead. Disabled channels will not receive new messages, but you can still view messages you already received. Are you sure you want to delete the channel? @@ -820,92 +820,92 @@ Are you sure you want to delete the channel? Êtes-vous sûr de vouloir supprimer ce canal ? - + Do you really want to remove this avatar? Voulez-vous vraiment enlever cet avatar ? - + You have already set an avatar for this address. Do you really want to overwrite it? Vous avez déjà mis un avatar pour cette adresse. Voulez-vous vraiment l’écraser ? - + Start-on-login not yet supported on your OS. Le démarrage dès l’ouverture de session n’est pas encore supporté sur votre OS. - + Minimize-to-tray not yet supported on your OS. La minimisation en zone système n’est pas encore supportée sur votre OS. - + Tray notifications not yet supported on your OS. Les notifications en zone système ne sont pas encore supportées sur votre OS. - + Testing... Tester… - + This is a chan address. You cannot use it as a pseudo-mailing list. Ceci est une adresse de canal. Vous ne pouvez pas l’utiliser en tant que pseudo liste de diffusion. - + The address should start with ''BM-'' L’adresse devrait commencer avec "BM-" - + The address is not typed or copied correctly (the checksum failed). L’adresse n’est pas correcte (la somme de contrôle a échoué). - + The version number of this address is higher than this software can support. Please upgrade Bitmessage. Le numéro de version de cette adresse est supérieur à celui que le programme peut supporter. Veuiller mettre Bitmessage à jour. - + The address contains invalid characters. L’adresse contient des caractères invalides. - + Some data encoded in the address is too short. Certaines données encodées dans l’adresse sont trop courtes. - + Some data encoded in the address is too long. Certaines données encodées dans l’adresse sont trop longues. - + Some data encoded in the address is malformed. Quelques données codées dans l’adresse sont mal formées. - + Enter an address above. Entrez ci-dessus une adresse. - + Address is an old type. We cannot display its past broadcasts. L’adresse est d’ancien type. Nous ne pouvons pas montrer ses messages de diffusion passés. - + There are no recent broadcasts from this address to display. Il n’y a aucun message de diffusion récent de cette adresse à afficher. - + You are using TCP port %1. (This can be changed in the settings). Vous utilisez le port TCP %1. (Ceci peut être changé dans les paramètres). @@ -1115,47 +1115,47 @@ Are you sure you want to delete the channel? Ajouter une nouvelle entrée - + Display the %1 recent broadcast(s) from this address. Montre le(s) %1 plus récent(s) message(s) de diffusion issu(s) de cette adresse. - + New version of PyBitmessage is available: %1. Download it from https://github.com/Bitmessage/PyBitmessage/releases/latest Une nouvelle version de PyBitmessage est disponible : %1. Veuillez la télécharger depuis https://github.com/Bitmessage/PyBitmessage/releases/latest - + Waiting for PoW to finish... %1% En attente de la fin de la PoW… %1% - + Shutting down Pybitmessage... %1% Pybitmessage en cours d’arrêt… %1% - + Waiting for objects to be sent... %1% En attente de l’envoi des objets… %1% - + Saving settings... %1% Enregistrement des paramètres… %1% - + Shutting down core... %1% Cœur en cours d’arrêt… %1% - + Stopping notifications... %1% Arrêt des notifications… %1% - + Shutdown imminent... %1% Arrêt imminent… %1% @@ -1165,17 +1165,17 @@ Are you sure you want to delete the channel? %n heure%n heures - + %n day(s) %n jour%n jours - + Shutting down PyBitmessage... %1% PyBitmessage en cours d’arrêt… %1% - + Sent Envoyé @@ -1279,7 +1279,7 @@ Difficulté requise du destinataire : %1 et %2 Problème : Vous essayez d’envoyer un message à un canal ou à vous-même mais votre clef de chiffrement n’a pas été trouvée dans le fichier keys.dat. Le message ne peut pas être chiffré. %1 - + Doing work necessary to send message. Travail en cours afin d’envoyer le message. @@ -1289,7 +1289,7 @@ Difficulté requise du destinataire : %1 et %2 Message envoyé. En attente de l’accusé de réception. Envoyé %1 - + Doing work necessary to request encryption key. Travail en cours afin d’obtenir la clé de chiffrement. @@ -1314,37 +1314,37 @@ Difficulté requise du destinataire : %1 et %2 Transfert de port UPnP retiré - + Mark all messages as read Marquer tous les messages comme lus - + Are you sure you would like to mark all messages read? Êtes-vous sûr(e) de vouloir marquer tous les messages comme lus ? - + Doing work necessary to send broadcast. Travail en cours afin d’envoyer la diffusion. - + Proof of work pending En attente de preuve de fonctionnement - + %n object(s) pending proof of work %n objet en attente de preuve de fonctionnement%n objet(s) en attente de preuve de fonctionnement - + %n object(s) waiting to be distributed %n objet en attente d'être distribué%n objet(s) en attente d'être distribués - + Wait until these tasks finish? Attendre jusqu'à ce que ces tâches se terminent ? @@ -1363,88 +1363,143 @@ Difficulté requise du destinataire : %1 et %2 The time on your computer, %1, may be wrong. Please verify your settings. L'heure sur votre ordinateur, %1, pourrait être faussse. Veuillez vérifier vos paramètres. + + + The name %1 was not found. + Le nom %1 n'a pas été trouvé. + + + + The namecoin query failed (%1) + La requête Namecoin a échouée (%1) + + + + The namecoin query failed. + La requête Namecoin a échouée. + + + + The name %1 has no valid JSON data. + Le nom %1 n'a aucune donnée JSON valide. + + + + The name %1 has no associated Bitmessage address. + Le nom %1 n'a aucune adresse Bitmessage d'associée. + + + + Success! Namecoind version %1 running. + Succès ! Namecoind version %1 en cours d'exécution. + + + + Success! NMControll is up and running. + Succès ! NMControll est debout et en cours d'exécution. + + + + Couldn't understand NMControl. + Ne pouvait pas comprendre NMControl. + Your GPU(s) did not calculate correctly, disabling OpenCL. Please report to the developers. Votre GPU(s) n'a pas calculé correctement, mettant OpenCL hors service. Veuillez remonter ceci aux développeurs s'il vous plaît. - + + + Welcome to easy and secure Bitmessage + * send messages to other people + * send broadcast messages like twitter or + * discuss in chan(nel)s with other people + + +Bienvenue dans le facile et sécurisé Bitmessage +* envoyer des messages à d'autres personnes +* envoyer des messages par diffusion (broadcast) à la manière de Twitter ou +* discuter dans des canaux (channels) avec d'autres personnes + + + + not recommended for chans pas recommandé pour les canaux - + Problems connecting? Try enabling UPnP in the Network Settings Des difficultés à se connecter ? Essayez de permettre UPnP dans les "Paramètres réseau" - + Error: Bitmessage addresses start with BM- Please check the recipient address %1 Erreur : Les adresses Bitmessage commencent par BM- Veuillez vérifier l'adresse du destinataire %1 - + Error: The recipient address %1 is not typed or copied correctly. Please check it. Erreur : L’adresse du destinataire %1 n’est pas correctement tapée ou recopiée. Veuillez la vérifier. - + Error: The recipient address %1 contains invalid characters. Please check it. Erreur : L’adresse du destinataire %1 contient des caractères invalides. Veuillez la vérifier. - + Error: The version of the recipient address %1 is too high. Either you need to upgrade your Bitmessage software or your acquaintance is being clever. Erreur : la version de l’adresse destinataire %1 est trop élevée. Vous devez mettre à niveau votre logiciel Bitmessage ou alors celui de votre connaissance est plus intelligent. - + Error: Some data encoded in the recipient address %1 is too short. There might be something wrong with the software of your acquaintance. Erreur : quelques données codées dans l’adresse destinataire %1 sont trop courtes. Il pourrait y avoir un soucis avec le logiciel de votre connaissance. - + Error: Some data encoded in the recipient address %1 is too long. There might be something wrong with the software of your acquaintance. Erreur : quelques données codées dans l’adresse destinataire %1 sont trop longues. Il pourrait y avoir un soucis avec le logiciel de votre connaissance. - + Error: Some data encoded in the recipient address %1 is malformed. There might be something wrong with the software of your acquaintance. Erreur : quelques données codées dans l’adresse destinataire %1 sont mal formées. Il pourrait y avoir un soucis avec le logiciel de votre connaissance. - + Error: Something is wrong with the recipient address %1. Erreur : quelque chose ne va pas avec l'adresse de destinataire %1. - + Synchronisation pending En attente de synchronisation - + Bitmessage hasn't synchronised with the network, %n object(s) to be downloaded. If you quit now, it may cause delivery delays. Wait until the synchronisation finishes? Bitmessage ne s'est pas synchronisé avec le réseau, %n objet(s) à télécharger. Si vous quittez maintenant, cela pourrait causer des retards de livraison. Attendre jusqu'à ce que la synchronisation aboutisse ?Bitmessage ne s'est pas synchronisé avec le réseau, %n objet(s) à télécharger. Si vous quittez maintenant, cela pourrait causer des retards de livraison. Attendre jusqu'à ce que la synchronisation aboutisse ? - + Not connected Non connecté - + Bitmessage isn't connected to the network. If you quit now, it may cause delivery delays. Wait until connected and the synchronisation finishes? Bitmessage n'est pas connecté au réseau. Si vous quittez maintenant, cela pourrait causer des retards de livraison. Attendre jusqu'à ce qu'il soit connecté et que la synchronisation se termine ? - + Waiting for network connection... En attente de connexion réseau... - + Waiting for finishing synchronisation... En attente d'achèvement de la synchronisation... @@ -2004,6 +2059,14 @@ The 'Random Number' option is selected by default but deterministic ad Module PoW C non disponible. Veuillez le construire. + + qrcodeDialog + + + QR-code + QR-code + + regenerateAddressesDialog From 4014b80b7cc0c9e80363455d75f050f1ec56ccb1 Mon Sep 17 00:00:00 2001 From: PyBitmessage Translations Date: Wed, 8 Mar 2017 17:25:19 +0100 Subject: [PATCH 016/407] Auto-updated language ja from transifex --- src/translations/bitmessage_ja.qm | Bin 65387 -> 65447 bytes src/translations/bitmessage_ja.ts | 312 +++++++++++++++--------------- 2 files changed, 160 insertions(+), 152 deletions(-) diff --git a/src/translations/bitmessage_ja.qm b/src/translations/bitmessage_ja.qm index 8bb153f722bb4e14ae205fea85013acba0c656ef..da6c715b15c0b1ffea8a707e3fcbaa5d83dafa2c 100644 GIT binary patch delta 694 zcmXAnZAep57{~wT?%q4^-KGpGF>sSeOhGp-oek6=MT=6Vs1?2tIhInIiPGyb%w7c1 z@f3uV^+7R(S!7~}(pL?PFeEjWnfaEPs2LTeo$$lq_rQ5x&h!7Dg$+&HrlxV5@(VyT z@H*ZTY+>`Bqp3lCkY3&a7FHm)Yk(Cav^go9B_3}ooARvX*nJ{0FZA)ksx7wEBd4we z_!5rX;!B(jIM$sAgi1J3VF$itAWyF2G*c~$6DA_4pPAVU@-Yi5CWByMtCS#^gxMg< z7c$s0@(ONYT2GOhLN-J7nn3oJS~aOkwI(4w1X#?VHBAzGLa}0c#3U1UbQ}jd!^MQ2 zWMJaAcrd*kXzmfyhJFAk*F@*jDE@pqdBs9zCxhhPHDZ-&s-3`$D%IQh<=GMG{{C>_ zZk*J#S^>NXB(D_4>S#nVD{eWfb&V?|(wby8Cds#}+`xfI`N837V0kZv$~NVd?91VO zrt--t+l+2)&7&kBDpPy&NDwevMqb&)+GxAhVjT91U;YjhwfJQvW&_h{l&!tRYRRYl z88c;8PZu=zVT(hhsypv1FutTV&8`EDqS~4GhwGe6l44`!l%TkbQ@Zrt z4Q`1?=h)=?e;%c7#mt`5h+P5G)xSPZ1A zY7)Ca{c4y}p#O0B9rx=xEvhc&qHJ9*8z8UF!cxemD+|BkzvS2rjCK2OnEZi3XMiPi zCm;_8#FSP7A14E12TXu(A64peSqBa2ZE{yJH{l)?_FB8_hS5Qb9u8z8#M)(Dw+{O8 j&jgttts^t?R9TTz%VT}{`Tt>Z@yRn~t}es-f<6BL=eXn( delta 670 zcmXAmYexHbK_~T`6i*qKU3Fnwbs>6jP7_MY3!fVwqw& zjzNf7fe{8%Nrqx+pM(m8pe!$~#JP-Ssg-^RFTLQW1LyEO&pGG!bVKW3*R}+TivSJ) zZ#ieEnYo>rslg542W|uYVF+DXV95iWEd}@;=X9mA9_O+x{_{IzUwNqtD>rLrGi;5e zfOi==C0BvjJe+eU0TDdTm&XJ1A;=dRRZBnBvpCI{6&hkjR!bgcX0fE#nAs~z)P!li z`N*z0#j44racE}8DOy{=o>Q|nh_%xbZK{}|O|*ssKSO9)o6IUGma|4pk8(Sn_D_{cYdB^S#t9)H;5#amCH^-~PvN8Vt z5fjiF$G80{2S&Z*;v<=j#&{!3r#0SakcEay2XHi6XixV6iyIUzSi~m5V^j5B6O{=T zL%Oc+VKNYtt-F~K49q2vTPSAb6sj{DIsy(a{Q?R*1I{F!Rg(kBuDivu$fsL5IOCTt z7IpyN-BRgvEbvIbSx4g~NB$3B@`cnkw+ggyQg_ln^}l;aFIre4rHI9b9@#p$uA*?t zSsUuzGo3Uf8kvJUqS2HmPgJR)b5rtU+C3l)^7P~Fz`%k$zbz8zb<6YNDz}L>ijXX( zD&_LY2dcv^rD7@p2)?USoC9rx~}A~N*YrvLQRPZ=K>A) Q?(aLMAAi$l{gk`!KSWyMV*mgE diff --git a/src/translations/bitmessage_ja.ts b/src/translations/bitmessage_ja.ts index ba546c48..1757b887 100644 --- a/src/translations/bitmessage_ja.ts +++ b/src/translations/bitmessage_ja.ts @@ -58,12 +58,12 @@ EmailGatewayRegistrationDialog - + Registration failed: 登録に失敗しました: - + The requested email address is not available, please try a new one. Fill out the new desired email address (including @mailchuck.com) below: リクエストしたメールアドレスは利用できません。新しいメールアドレスをお試しください。 新しい希望メールアドレス (@mailchuck.com を含む) を次のように記入してください: @@ -168,52 +168,52 @@ Please type the desired email address (including @mailchuck.com) below: MainWindow - + Reply to sender 送信元に返信 - + Reply to channel チャンネルに返信 - + Add sender to your Address Book 送信元をアドレス帳に追加 - + Add sender to your Blacklist 送信元をブラックリストに追加 - + Move to Trash ゴミ箱へ移動 - + Undelete 削除を元に戻す - + View HTML code as formatted text HTMLコードを整形したテキストで表示 - + Save message as... 形式を選択してメッセージを保存 - + Mark Unread 未読にする - + New 新規 @@ -238,12 +238,12 @@ Please type the desired email address (including @mailchuck.com) below: アドレスをコピー - + Special address behavior... アドレスの特別な動作 - + Email gateway メールゲートウェイ @@ -253,37 +253,37 @@ Please type the desired email address (including @mailchuck.com) below: 削除 - + Send message to this address このアドレスへ送信 - + Subscribe to this address このアドレスを購読 - + Add New Address アドレスを追加 - + Copy destination address to clipboard 宛先アドレスをコピー - + Force send 強制的に送信 - + One of your addresses, %1, is an old version 1 address. Version 1 addresses are no longer supported. May we delete it now? %1は古いバージョン1のアドレスです。バージョン1のアドレスはサポートが終了しています。すぐに削除しますか? - + Waiting for their encryption key. Will request it again soon. 暗号鍵を待っています。 すぐにもう一度リクエストします。 @@ -293,17 +293,17 @@ Please type the desired email address (including @mailchuck.com) below: - + Queued. キューに入りました。 - + Message sent. Waiting for acknowledgement. Sent at %1 メッセージを送信しました。 確認応答を待っています。 %1 で送信されました - + Message sent. Sent at %1 メッセージは送信されました。送信先: %1 @@ -313,47 +313,47 @@ Please type the desired email address (including @mailchuck.com) below: - + Acknowledgement of the message received %1 メッセージの確認を受け取りました %1 - + Broadcast queued. 配信がキューに入りました。 - + Broadcast on %1 配信: %1 - + Problem: The work demanded by the recipient is more difficult than you are willing to do. %1 問題: 受信者が要求している処理は現在あなたが設定しているよりも高い難易度です。 %1 - + Problem: The recipient's encryption key is no good. Could not encrypt message. %1 問題: 受信者の暗号鍵は正当でない物です。メッセージを暗号化できません。 %1 - + Forced difficulty override. Send should start soon. 難易度を強制上書きしました。まもなく送信されます。 - + Unknown status: %1 %2 不明なステータス: %1 %2 - + Not Connected 未接続 - + Show Bitmessage Bitmessageを表示 @@ -363,12 +363,12 @@ Please type the desired email address (including @mailchuck.com) below: 送る - + Subscribe 購読 - + Channel チャンネル @@ -378,66 +378,66 @@ Please type the desired email address (including @mailchuck.com) below: 終了 - + You may manage your keys by editing the keys.dat file stored in the same directory as this program. It is important that you back up this file. プログラムを同じディレクトリに保存されているkeys.datファイルを編集することで鍵を管理できます。ファイルをバックアップしておくことも重要です。 - + You may manage your keys by editing the keys.dat file stored in %1 It is important that you back up this file. %1に保存されているkeys.datファイルを編集することで鍵を管理できます。ファイルをバックアップしておくことも重要です。 - + Open keys.dat? keys.datを開きますか? - + You may manage your keys by editing the keys.dat file stored in the same directory as this program. It is important that you back up this file. Would you like to open the file now? (Be sure to close Bitmessage before making any changes.) プログラムを同じディレクトリに保存されているkeys.datファイルを編集することで鍵を管理できます。ファイルをバックアップしておくことも重要です。すぐにファイルを開きますか?(必ず編集する前にBitmessageを終了してください) - + You may manage your keys by editing the keys.dat file stored in %1 It is important that you back up this file. Would you like to open the file now? (Be sure to close Bitmessage before making any changes.) %1に保存されているkeys.datファイルを編集することで鍵を管理できます。ファイルをバックアップしておくことも重要です。すぐにファイルを開きますか?(必ず編集する前にBitmessageを終了してください) - + Delete trash? ゴミ箱を空にしますか? - + Are you sure you want to delete all trashed messages? ゴミ箱内のメッセージを全て削除してもよろしいですか? - + bad passphrase 不正なパスフレーズ - + You must type your passphrase. If you don't have one then this is not the form for you. パスフレーズを入力してください。パスフレーズがない場合は入力する必要はありません。 - + Bad address version number 不正なアドレスのバージョン番号 - + Your address version number must be a number: either 3 or 4. アドレスのバージョン番号は数字にする必要があります: 3 または 4。 - + Your address version number must be either 3 or 4. アドレスのバージョン番号は、3 または 4 のどちらかにする必要があります。 @@ -507,22 +507,22 @@ It is important that you back up this file. Would you like to open the file now? - + Connection lost 接続が切断されました - + Connected 接続済み - + Message trashed メッセージが削除されました - + The TTL, or Time-To-Live is the length of time that the network will hold the message. The recipient must get it during this time. If your Bitmessage client does not hear an acknowledgement, it will resend the message automatically. The longer the Time-To-Live, the @@ -533,17 +533,17 @@ It is important that you back up this file. Would you like to open the file now? コンピュータがメッセージを送信するために必要な処理が増えます。 多くの場合 4〜5 日のTTL(Time-To-Live)が適切です。 - + Message too long メッセージが長すぎます - + The message that you are trying to send is too long by %1 bytes. (The maximum is 261644 bytes). Please cut it down before sending. 送信しようとしているメッセージが %1 バイト長すぎます。 (最大は261644バイトです)。 送信する前に短くしてください。 - + Error: Your account wasn't registered at an email gateway. Sending registration now as %1, please wait for the registration to be processed before retrying sending. エラー: アカウントがメールゲートウェイに登録されていません。 今 %1 として登録を送信しています。送信を再試行する前に、登録が処理されるまでお待ちください。 @@ -588,67 +588,67 @@ It is important that you back up this file. Would you like to open the file now? - + Error: You must specify a From address. If you don't have one, go to the 'Your Identities' tab. エラー: 送信元アドレスを指定してください。まだ作成していない場合には「アドレス一覧」のタブを開いてください。 - + Address version number アドレスのバージョン番号 - + Concerning the address %1, Bitmessage cannot understand address version numbers of %2. Perhaps upgrade Bitmessage to the latest version. アドレス %1 に接続。%2 のバージョン番号は処理できません。Bitmessageを最新のバージョンへアップデートしてください。 - + Stream number ストリーム番号 - + Concerning the address %1, Bitmessage cannot handle stream numbers of %2. Perhaps upgrade Bitmessage to the latest version. アドレス %1 に接続。%2 のストリーム番号は処理できません。Bitmessageを最新のバージョンへアップデートしてください。 - + Warning: You are currently not connected. Bitmessage will do the work necessary to send the message but it won't send until you connect. 警告: 接続されていません。Bitmessageはメッセージの処理を行いますが、ネットワークに接続するまで送信はされません。 - + Message queued. メッセージがキューに入りました。 - + Your 'To' field is empty. 宛先が指定されていません。 - + Right click one or more entries in your address book and select 'Send message to this address'. アドレス帳から一つ、または複数のアドレスを右クリックして「このアドレスへ送信」を選んでください。 - + Fetched address from namecoin identity. namecoin IDからアドレスを取得。 - + New Message 新規メッセージ - + From 送信元 - + Sending email gateway registration request メールゲートウェイの登録リクエストを送信しています @@ -663,142 +663,142 @@ It is important that you back up this file. Would you like to open the file now? 入力されたアドレスは不正です。無視されました。 - + Error: You cannot add the same address to your address book twice. Try renaming the existing one if you want. エラー: 同じアドレスを複数アドレス帳に追加する事はできません。既存の項目をリネームしてください。 - + Error: You cannot add the same address to your subscriptions twice. Perhaps rename the existing one if you want. エラー: 購読に、同じアドレスを2回追加することはできません。 必要に応じて、既存の名前を変更してください。 - + Restart 再開 - + You must restart Bitmessage for the port number change to take effect. ポート番号の変更を有効にするにはBitmessageを再起動してください。 - + Bitmessage will use your proxy from now on but you may want to manually restart Bitmessage now to close existing connections (if any). プロキシの設定を有効にするには手動でBitmessageを再起動してください。既に接続がある場合は切断されます。 - + Number needed 数字が必要です - + Your maximum download and upload rate must be numbers. Ignoring what you typed. 最大ダウンロード数とアップロード数は数字にする必要があります。 入力されたものを無視します。 - + Will not resend ever 今後再送信されません - + Note that the time limit you entered is less than the amount of time Bitmessage waits for the first resend attempt therefore your messages will never be resent. 入力した時間制限は、Bitmessageが最初の再送試行を待つ時間よりも短いため、メッセージは再送信されないことにご注意ください。 - + Sending email gateway unregistration request メールゲートウェイの登録抹消リクエストを送信しています - + Sending email gateway status request メールゲートウェイの状態リクエストを送信しています - + Passphrase mismatch パスフレーズが一致しません - + The passphrase you entered twice doesn't match. Try again. 再度入力されたパスフレーズが一致しません。再入力してください。 - + Choose a passphrase パスフレーズを選択してください - + You really do need a passphrase. パスフレーズが必要です。 - + Address is gone アドレスが無効になりました - + Bitmessage cannot find your address %1. Perhaps you removed it? アドレス %1 が見つかりません。既に削除していませんか? - + Address disabled アドレスが無効になりました - + Error: The address from which you are trying to send is disabled. You'll have to enable it on the 'Your Identities' tab before using it. エラー: 送信しようとしたアドレスは無効になっています。使用する前に「アドレス一覧」で有効にしてください。 - + Entry added to the Address Book. Edit the label to your liking. アドレス帳に項目が追加されました。ラベルは自由に編集できます。 - + Entry added to the blacklist. Edit the label to your liking. ブラックリストに項目が追加されました。ラベルは自由に編集できます。 - + Error: You cannot add the same address to your blacklist twice. Try renaming the existing one if you want. エラー: ブラックリストに同じアドレスを2回追加することはできません。 必要に応じて既存の名前を変更してみてください。 - + Moved items to trash. アイテムをゴミ箱へ移動。 - + Undeleted item. アイテムの削除を元に戻します。 - + Save As... 形式を選択して保存 - + Write error. 書き込みエラー。 - + No addresses selected. アドレスが未選択です。 - + If you delete the subscription, messages that you already received will become inaccessible. Maybe you can consider disabling the subscription instead. Disabled subscriptions will not receive new messages, but you can still view messages you already received. Are you sure you want to delete the subscription? @@ -807,7 +807,7 @@ Are you sure you want to delete the subscription? 購読を削除してもよろしいですか? - + If you delete the channel, messages that you already received will become inaccessible. Maybe you can consider disabling the channel instead. Disabled channels will not receive new messages, but you can still view messages you already received. Are you sure you want to delete the channel? @@ -816,92 +816,92 @@ Are you sure you want to delete the channel? チャンネルを削除してもよろしいですか? - + Do you really want to remove this avatar? このアバターを削除してもよろしいですか? - + You have already set an avatar for this address. Do you really want to overwrite it? すでにこのアドレスのアバターを設定しています。 上書きしてもよろしいですか? - + Start-on-login not yet supported on your OS. ログイン時に開始は、まだお使いのOSでサポートされていません。 - + Minimize-to-tray not yet supported on your OS. トレイに最小化は、まだお使いのOSでサポートされていません。 - + Tray notifications not yet supported on your OS. トレイ通知は、まだお使いのOSでサポートされていません。 - + Testing... テスト中 - + This is a chan address. You cannot use it as a pseudo-mailing list. chanアドレスは仮想メーリングリストのアドレスには使用できません。 - + The address should start with ''BM-'' アドレスは「BM-」から始まります - + The address is not typed or copied correctly (the checksum failed). このアドレスは正しく入力、またはコピーされていません。(チェックサムが一致しません)。 - + The version number of this address is higher than this software can support. Please upgrade Bitmessage. このアドレスのバージョン番号はこのプログラムのサポート範囲外です。Bitmessageをアップデートしてください。 - + The address contains invalid characters. 入力されたアドレスは不正な文字を含んでいます。 - + Some data encoded in the address is too short. このアドレスでエンコードされたデータが短すぎます。 - + Some data encoded in the address is too long. このアドレスでエンコードされたデータが長過ぎます。 - + Some data encoded in the address is malformed. このアドレスでエンコードされた一部のデータが不正です。 - + Enter an address above. 上にアドレスを入力してください。 - + Address is an old type. We cannot display its past broadcasts. アドレスが古い形式です。 過去の配信は表示できません。 - + There are no recent broadcasts from this address to display. このアドレスから表示する最近の配信はありません。 - + You are using TCP port %1. (This can be changed in the settings). 使用中のポート %1 (設定で変更できます)。 @@ -1111,47 +1111,47 @@ Are you sure you want to delete the channel? 新しい項目を追加 - + Display the %1 recent broadcast(s) from this address. このアドレスから%1の最新の配信を表示します。 - + New version of PyBitmessage is available: %1. Download it from https://github.com/Bitmessage/PyBitmessage/releases/latest 新しいバージョンの PyBitmessage が利用可能です: %1。 https://github.com/Bitmessage/PyBitmessage/releases/latest からダウンロードしてください - + Waiting for PoW to finish... %1% PoW(証明)が完了するのを待っています... %1% - + Shutting down Pybitmessage... %1% Pybitmessageをシャットダウンしています... %1% - + Waiting for objects to be sent... %1% オブジェクトの送信待ち... %1% - + Saving settings... %1% 設定を保存しています... %1% - + Shutting down core... %1% コアをシャットダウンしています... %1% - + Stopping notifications... %1% 通知を停止しています... %1% - + Shutdown imminent... %1% すぐにシャットダウンします... %1% @@ -1161,17 +1161,17 @@ Are you sure you want to delete the channel? %n 時間 - + %n day(s) %n 日 - + Shutting down PyBitmessage... %1% PyBitmessageをシャットダウンしています... %1% - + Sent 送信済 @@ -1275,7 +1275,7 @@ Receiver's required difficulty: %1 and %2 問題: あなた自身またはチャンネルにメッセージを送信しようとしていますが、暗号鍵がkeys.datファイルに見つかりませんでした。 メッセージを暗号化できませんでした。 %1 - + Doing work necessary to send message. メッセージの送信に必要な処理を行っています。 @@ -1285,7 +1285,7 @@ Receiver's required difficulty: %1 and %2 メッセージを送信しました。 確認応答を待っています。 %1 で送信しました - + Doing work necessary to request encryption key. 暗号鍵のリクエストに必要な処理を行っています。 @@ -1310,37 +1310,37 @@ Receiver's required difficulty: %1 and %2 UPnPポートマッピングを削除しました - + Mark all messages as read すべてのメッセージを既読にする - + Are you sure you would like to mark all messages read? すべてのメッセージを既読にしてもよろしいですか? - + Doing work necessary to send broadcast. 配信に必要な処理を行っています。 - + Proof of work pending PoW(証明)を待っています - + %n object(s) pending proof of work %n オブジェクトが証明待ち (PoW) - + %n object(s) waiting to be distributed %n オブジェクトが配布待ち - + Wait until these tasks finish? これらのタスクが完了するまで待ちますか? @@ -1405,7 +1405,7 @@ Receiver's required difficulty: %1 and %2 GPUが正しく求められないため、OpenCLが無効になりました。 開発者に報告してください。 - + Welcome to easy and secure Bitmessage * send messages to other people @@ -1420,82 +1420,82 @@ Receiver's required difficulty: %1 and %2 - + not recommended for chans チャンネルにはお勧めしません - + Problems connecting? Try enabling UPnP in the Network Settings 接続に問題がありますか? ネットワーク設定でUPnPを有効にしてみてください - + Error: Bitmessage addresses start with BM- Please check the recipient address %1 エラー: BitmessageのアドレスはBM-で始まります。 受信者のアドレス %1 を確認してください - + Error: The recipient address %1 is not typed or copied correctly. Please check it. エラー: 受信者のアドレス %1 は正しく入力、またはコピーされていません。確認して下さい。 - + Error: The recipient address %1 contains invalid characters. Please check it. エラー: 受信者のアドレス %1 は不正な文字を含んでいます。確認して下さい。 - + Error: The version of the recipient address %1 is too high. Either you need to upgrade your Bitmessage software or your acquaintance is being clever. エラー: 受信者アドレスのバージョン %1 は高すぎます。 Bitmessageソフトウェアをアップグレードする必要があるか、連絡先が賢明になっているかのいずれかです。 - + Error: Some data encoded in the recipient address %1 is too short. There might be something wrong with the software of your acquaintance. エラー: アドレス %1 でエンコードされたデータが短すぎます。連絡先のソフトウェアが何かしら誤っている可能性があります。 - + Error: Some data encoded in the recipient address %1 is too long. There might be something wrong with the software of your acquaintance. エラー: 受信者のアドレス %1 でエンコードされたデータが短すぎます。連絡先のソフトウェアが何かしら誤っている可能性があります。 - + Error: Some data encoded in the recipient address %1 is malformed. There might be something wrong with the software of your acquaintance. エラー: 受信者のアドレス %1 でエンコードされたデータの一部が不正です。連絡先のソフトウェアが何かしら誤っている可能性があります。 - + Error: Something is wrong with the recipient address %1. エラー: 受信者のアドレス %1 には何かしら誤りがあります。 - + Synchronisation pending 同期を保留しています - + Bitmessage hasn't synchronised with the network, %n object(s) to be downloaded. If you quit now, it may cause delivery delays. Wait until the synchronisation finishes? Bitmessageはネットワークと同期していません。%n のオブジェクトをダウンロードする必要があります。 今、終了すると、配送が遅れることがあります。 同期が完了するまで待ちますか? - + Not connected 未接続 - + Bitmessage isn't connected to the network. If you quit now, it may cause delivery delays. Wait until connected and the synchronisation finishes? Bitmessageはネットワークに接続していません。 今、終了すると、配送が遅れることがあります。 接続して、同期が完了するまで待ちますか? - + Waiting for network connection... ネットワーク接続を待っています... - + Waiting for finishing synchronisation... 同期の完了を待っています... @@ -2055,6 +2055,14 @@ The 'Random Number' option is selected by default but deterministic ad C PoW モジュールが利用できません。ビルドしてください。 + + qrcodeDialog + + + QR-code + QR コード + + regenerateAddressesDialog From 3ac67e5da7ddfe2d2f3579b1f5bea7b9231cddf5 Mon Sep 17 00:00:00 2001 From: Peter Surda Date: Thu, 9 Mar 2017 11:26:44 +0100 Subject: [PATCH 017/407] Connection error reporting changes - fewer tracebacks - more standardised reports including errno --- src/class_receiveDataThread.py | 10 +++++++--- src/class_sendDataThread.py | 4 ++-- 2 files changed, 9 insertions(+), 5 deletions(-) diff --git a/src/class_receiveDataThread.py b/src/class_receiveDataThread.py index 90364228..124e06c0 100644 --- a/src/class_receiveDataThread.py +++ b/src/class_receiveDataThread.py @@ -123,11 +123,11 @@ class receiveDataThread(threading.Thread): select.select([self.sslSock if isSSL else self.sock], [], [], 10) logger.debug('sock.recv retriable error') continue - logger.error('sock.recv error. Closing receiveData thread (' + str(self.peer) + ', Thread ID: ' + str(id(self)) + ').' + str(err.errno) + "/" + str(err)) + logger.error('sock.recv error. Closing receiveData thread, %s', str(err)) break # print 'Received', repr(self.data) if len(self.data) == dataLen: # If self.sock.recv returned no data: - logger.debug('Connection to ' + str(self.peer) + ' closed. Closing receiveData thread. (ID: ' + str(id(self)) + ')') + logger.debug('Connection to ' + str(self.peer) + ' closed. Closing receiveData thread') break else: self.processData() @@ -302,9 +302,13 @@ class receiveDataThread(threading.Thread): logger.debug("Waiting for SSL socket handhake write") select.select([], [self.sslSock], [], 10) continue - logger.error("SSL socket handhake failed: %s, shutting down connection", str(e)) + logger.error("SSL socket handhake failed: shutting down connection, %s", str(e)) self.sendDataThreadQueue.put((0, 'shutdown','tls handshake fail %s' % (str(e)))) return False + except socket.error as err: + logger.debug('SSL socket handshake failed, shutting down connection, %s', str(err)) + self.sendDataThreadQueue.put((0, 'shutdown','tls handshake fail')) + return False except Exception: logger.error("SSL socket handhake failed, shutting down connection", exc_info=True) self.sendDataThreadQueue.put((0, 'shutdown','tls handshake fail')) diff --git a/src/class_sendDataThread.py b/src/class_sendDataThread.py index 7de63a02..ac11c6b2 100644 --- a/src/class_sendDataThread.py +++ b/src/class_sendDataThread.py @@ -105,8 +105,8 @@ class sendDataThread(threading.Thread): select.select([], [self.sslSock if isSSL else self.sock], [], 10) logger.debug('sock.recv retriable error') continue - if e.errno in (errno.EPIPE, errno.ECONNRESET, errno.EHOSTUNREACH, errno.ETIMEDOUT): - logger.debug('Connection error (EPIPE/ECONNRESET/EHOSTUNREACH/ETIMEDOUT)') + if e.errno in (errno.EPIPE, errno.ECONNRESET, errno.EHOSTUNREACH, errno.ETIMEDOUT, errno.ECONNREFUSED): + logger.debug('Connection error: %s', str(e)) return False raise throttle.SendThrottle().wait(amountSent) From 998935be5f91b02279eaa343960298a68f415ce2 Mon Sep 17 00:00:00 2001 From: Peter Surda Date: Fri, 10 Mar 2017 23:11:57 +0100 Subject: [PATCH 018/407] New network subsystem, WIP - finished proxy design - socks4a and socks5 implemented - authentication not tested - resolver for both socks4a and socks5 - http client example using the proxy --- src/network/advanceddispatcher.py | 23 +- src/network/asyncore_pollchoose.py | 723 +++++++++++++++++++++++++++++ src/network/http-old.py | 49 ++ src/network/http.py | 111 +++-- src/network/proxy.py | 173 +------ src/network/socks4a.py | 104 +++++ src/network/socks5.py | 170 +++++++ 7 files changed, 1146 insertions(+), 207 deletions(-) create mode 100644 src/network/asyncore_pollchoose.py create mode 100644 src/network/http-old.py create mode 100644 src/network/socks4a.py create mode 100644 src/network/socks5.py diff --git a/src/network/advanceddispatcher.py b/src/network/advanceddispatcher.py index 8258412a..9ec7f496 100644 --- a/src/network/advanceddispatcher.py +++ b/src/network/advanceddispatcher.py @@ -1,12 +1,13 @@ -import asyncore +import asyncore_pollchoose as asyncore class AdvancedDispatcher(asyncore.dispatcher): _buf_len = 131072 - def __init__(self, sock): - asyncore.dispatcher.__init__(self, sock) - self.read_buf = "" - self.write_buf = "" + def __init__(self): + if not hasattr(self, '_map'): + asyncore.dispatcher.__init__(self) + self.read_buf = b"" + self.write_buf = b"" self.state = "init" def slice_read_buf(self, length=0): @@ -22,7 +23,7 @@ class AdvancedDispatcher(asyncore.dispatcher): return True def process(self): - if len(self.read_buf) == 0: + if self.state != "init" and len(self.read_buf) == 0: return while True: try: @@ -37,10 +38,10 @@ class AdvancedDispatcher(asyncore.dispatcher): self.state = state def writable(self): - return len(self.write_buf) > 0 + return self.connecting or len(self.write_buf) > 0 def readable(self): - return len(self.read_buf) < AdvancedDispatcher._buf_len + return self.connecting or len(self.read_buf) < AdvancedDispatcher._buf_len def handle_read(self): self.read_buf += self.recv(AdvancedDispatcher._buf_len) @@ -49,4 +50,8 @@ class AdvancedDispatcher(asyncore.dispatcher): def handle_write(self): written = self.send(self.write_buf) self.slice_write_buf(written) -# self.process() + + def handle_connect(self): + self.process() + + diff --git a/src/network/asyncore_pollchoose.py b/src/network/asyncore_pollchoose.py new file mode 100644 index 00000000..19ec9f42 --- /dev/null +++ b/src/network/asyncore_pollchoose.py @@ -0,0 +1,723 @@ +# -*- Mode: Python -*- +# Id: asyncore.py,v 2.51 2000/09/07 22:29:26 rushing Exp +# Author: Sam Rushing + +# ====================================================================== +# Copyright 1996 by Sam Rushing +# +# All Rights Reserved +# +# Permission to use, copy, modify, and distribute this software and +# its documentation for any purpose and without fee is hereby +# granted, provided that the above copyright notice appear in all +# copies and that both that copyright notice and this permission +# notice appear in supporting documentation, and that the name of Sam +# Rushing not be used in advertising or publicity pertaining to +# distribution of the software without specific, written prior +# permission. +# +# SAM RUSHING DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, +# INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN +# NO EVENT SHALL SAM RUSHING BE LIABLE FOR ANY SPECIAL, INDIRECT OR +# CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS +# OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, +# NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN +# CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +# ====================================================================== + +"""Basic infrastructure for asynchronous socket service clients and servers. + +There are only two ways to have a program on a single processor do "more +than one thing at a time". Multi-threaded programming is the simplest and +most popular way to do it, but there is another very different technique, +that lets you have nearly all the advantages of multi-threading, without +actually using multiple threads. it's really only practical if your program +is largely I/O bound. If your program is CPU bound, then pre-emptive +scheduled threads are probably what you really need. Network servers are +rarely CPU-bound, however. + +If your operating system supports the select() system call in its I/O +library (and nearly all do), then you can use it to juggle multiple +communication channels at once; doing other work while your I/O is taking +place in the "background." Although this strategy can seem strange and +complex, especially at first, it is in many ways easier to understand and +control than multi-threaded programming. The module documented here solves +many of the difficult problems for you, making the task of building +sophisticated high-performance network servers and clients a snap. +""" + +import select +import socket +import sys +import time +import warnings + +import os +from errno import EALREADY, EINPROGRESS, EWOULDBLOCK, ECONNRESET, EINVAL, \ + ENOTCONN, ESHUTDOWN, EISCONN, EBADF, ECONNABORTED, EPIPE, EAGAIN, \ + errorcode + +_DISCONNECTED = frozenset((ECONNRESET, ENOTCONN, ESHUTDOWN, ECONNABORTED, EPIPE, + EBADF)) + +try: + socket_map +except NameError: + socket_map = {} + +def _strerror(err): + try: + return os.strerror(err) + except (ValueError, OverflowError, NameError): + if err in errorcode: + return errorcode[err] + return "Unknown error %s" %err + +class ExitNow(Exception): + pass + +_reraised_exceptions = (ExitNow, KeyboardInterrupt, SystemExit) + +def read(obj): + try: + obj.handle_read_event() + except _reraised_exceptions: + raise + except: + obj.handle_error() + +def write(obj): + try: + obj.handle_write_event() + except _reraised_exceptions: + raise + except: + obj.handle_error() + +def _exception(obj): + try: + obj.handle_expt_event() + except _reraised_exceptions: + raise + except: + obj.handle_error() + +def readwrite(obj, flags): + try: + if flags & select.POLLIN: + obj.handle_read_event() + if flags & select.POLLOUT: + obj.handle_write_event() + if flags & select.POLLPRI: + obj.handle_expt_event() + if flags & (select.POLLHUP | select.POLLERR | select.POLLNVAL): + obj.handle_close() + except socket.error as e: + if e.args[0] not in _DISCONNECTED: + obj.handle_error() + else: + obj.handle_close() + except _reraised_exceptions: + raise + except: + obj.handle_error() + +def select_poller(timeout=0.0, map=None): + """A poller which uses select(), available on most platforms.""" + if map is None: + map = socket_map + if map: + r = []; w = []; e = [] + for fd, obj in list(map.items()): + is_r = obj.readable() + is_w = obj.writable() + if is_r: + r.append(fd) + # accepting sockets should not be writable + if is_w and not obj.accepting: + w.append(fd) + if is_r or is_w: + e.append(fd) + if [] == r == w == e: + time.sleep(timeout) + return + + try: + r, w, e = select.select(r, w, e, timeout) + except KeyboardInterrupt: + return + + for fd in r: + obj = map.get(fd) + if obj is None: + continue + read(obj) + + for fd in w: + obj = map.get(fd) + if obj is None: + continue + write(obj) + + for fd in e: + obj = map.get(fd) + if obj is None: + continue + _exception(obj) + +def poll_poller(timeout=0.0, map=None): + """A poller which uses poll(), available on most UNIXen.""" + if map is None: + map = socket_map + if timeout is not None: + # timeout is in milliseconds + timeout = int(timeout*1000) + pollster = select.poll() + if map: + for fd, obj in list(map.items()): + flags = 0 + if obj.readable(): + flags |= select.POLLIN | select.POLLPRI + # accepting sockets should not be writable + if obj.writable() and not obj.accepting: + flags |= select.POLLOUT + if flags: + pollster.register(fd, flags) + try: + r = pollster.poll(timeout) + except KeyboardInterrupt: + r = [] + for fd, flags in r: + obj = map.get(fd) + if obj is None: + continue + readwrite(obj, flags) + +# Aliases for backward compatibility +poll = select_poller +poll2 = poll3 = poll_poller + +def epoll_poller(timeout=0.0, map=None): + """A poller which uses epoll(), supported on Linux 2.5.44 and newer.""" + if map is None: + map = socket_map + pollster = select.epoll() + if map: + for fd, obj in map.items(): + flags = 0 + if obj.readable(): + flags |= select.POLLIN | select.POLLPRI + if obj.writable(): + flags |= select.POLLOUT + if flags: + # Only check for exceptions if object was either readable + # or writable. + flags |= select.POLLERR | select.POLLHUP | select.POLLNVAL + pollster.register(fd, flags) + try: + r = pollster.poll(timeout) + except select.error, err: + if err.args[0] != EINTR: + raise + r = [] + for fd, flags in r: + obj = map.get(fd) + if obj is None: + continue + readwrite(obj, flags) + +def kqueue_poller(timeout=0.0, map=None): + """A poller which uses kqueue(), BSD specific.""" + if map is None: + map = socket_map + if map: + kqueue = select.kqueue() + flags = select.KQ_EV_ADD | select.KQ_EV_ENABLE + selectables = 0 + for fd, obj in map.items(): + filter = 0 + if obj.readable(): + filter |= select.KQ_FILTER_READ + if obj.writable(): + filter |= select.KQ_FILTER_WRITE + if filter: + ev = select.kevent(fd, filter=filter, flags=flags) + kqueue.control([ev], 0) + selectables += 1 + + events = kqueue.control(None, selectables, timeout) + for event in events: + fd = event.ident + obj = map.get(fd) + if obj is None: + continue + if event.filter == select.KQ_FILTER_READ: + read(obj) + if event.filter == select.KQ_FILTER_WRITE: + write(obj) + kqueue.close() + + +def loop(timeout=30.0, use_poll=False, map=None, count=None, + poller=select_poller): + if map is None: + map = socket_map + # code which grants backward compatibility with "use_poll" + # argument which should no longer be used in favor of + # "poller" + if use_poll and hasattr(select, 'poll'): + poller = poll_poller + else: + poller = select_poller + + if count is None: + while map: + poller(timeout, map) + else: + while map and count > 0: + poller(timeout, map) + count = count - 1 + +class dispatcher: + + debug = False + connected = False + accepting = False + connecting = False + closing = False + addr = None + ignore_log_types = frozenset(['warning']) + + def __init__(self, sock=None, map=None): + if map is None: + self._map = socket_map + else: + self._map = map + + self._fileno = None + + if sock: + # Set to nonblocking just to make sure for cases where we + # get a socket from a blocking source. + sock.setblocking(0) + self.set_socket(sock, map) + self.connected = True + # The constructor no longer requires that the socket + # passed be connected. + try: + self.addr = sock.getpeername() + except socket.error as err: + if err.args[0] in (ENOTCONN, EINVAL): + # To handle the case where we got an unconnected + # socket. + self.connected = False + else: + # The socket is broken in some unknown way, alert + # the user and remove it from the map (to prevent + # polling of broken sockets). + self.del_channel(map) + raise + else: + self.socket = None + + def __repr__(self): + status = [self.__class__.__module__+"."+self.__class__.__name__] + if self.accepting and self.addr: + status.append('listening') + elif self.connected: + status.append('connected') + if self.addr is not None: + try: + status.append('%s:%d' % self.addr) + except TypeError: + status.append(repr(self.addr)) + return '<%s at %#x>' % (' '.join(status), id(self)) + + __str__ = __repr__ + + def add_channel(self, map=None): + #self.log_info('adding channel %s' % self) + if map is None: + map = self._map + map[self._fileno] = self + + def del_channel(self, map=None): + fd = self._fileno + if map is None: + map = self._map + if fd in map: + #self.log_info('closing channel %d:%s' % (fd, self)) + del map[fd] + self._fileno = None + + def create_socket(self, family=socket.AF_INET, type=socket.SOCK_STREAM): + self.family_and_type = family, type + sock = socket.socket(family, type) + sock.setblocking(0) + self.set_socket(sock) + + def set_socket(self, sock, map=None): + self.socket = sock +## self.__dict__['socket'] = sock + self._fileno = sock.fileno() + self.add_channel(map) + + def set_reuse_addr(self): + # try to re-use a server port if possible + try: + self.socket.setsockopt( + socket.SOL_SOCKET, socket.SO_REUSEADDR, + self.socket.getsockopt(socket.SOL_SOCKET, + socket.SO_REUSEADDR) | 1 + ) + except socket.error: + pass + + # ================================================== + # predicates for select() + # these are used as filters for the lists of sockets + # to pass to select(). + # ================================================== + + def readable(self): + return True + + def writable(self): + return True + + # ================================================== + # socket object methods. + # ================================================== + + def listen(self, num): + self.accepting = True + if os.name == 'nt' and num > 5: + num = 5 + return self.socket.listen(num) + + def bind(self, addr): + self.addr = addr + return self.socket.bind(addr) + + def connect(self, address): + self.connected = False + self.connecting = True + err = self.socket.connect_ex(address) + if err in (EINPROGRESS, EALREADY, EWOULDBLOCK) \ + or err == EINVAL and os.name in ('nt', 'ce'): + self.addr = address + return + if err in (0, EISCONN): + self.addr = address + self.handle_connect_event() + else: + raise socket.error(err, errorcode[err]) + + def accept(self): + # XXX can return either an address pair or None + try: + conn, addr = self.socket.accept() + except TypeError: + return None + except socket.error as why: + if why.args[0] in (EWOULDBLOCK, ECONNABORTED, EAGAIN): + return None + else: + raise + else: + return conn, addr + + def send(self, data): + try: + result = self.socket.send(data) + return result + except socket.error as why: + if why.args[0] == EWOULDBLOCK: + return 0 + elif why.args[0] in _DISCONNECTED: + self.handle_close() + return 0 + else: + raise + + def recv(self, buffer_size): + try: + data = self.socket.recv(buffer_size) + if not data: + # a closed connection is indicated by signaling + # a read condition, and having recv() return 0. + self.handle_close() + return b'' + else: + return data + except socket.error as why: + # winsock sometimes raises ENOTCONN + if why.args[0] in _DISCONNECTED: + self.handle_close() + return b'' + else: + raise + + def close(self): + self.connected = False + self.accepting = False + self.connecting = False + self.del_channel() + try: + self.socket.close() + except socket.error as why: + if why.args[0] not in (ENOTCONN, EBADF): + raise + + # cheap inheritance, used to pass all other attribute + # references to the underlying socket object. + def __getattr__(self, attr): + try: + retattr = getattr(self.socket, attr) + except AttributeError: + raise AttributeError("%s instance has no attribute '%s'" + %(self.__class__.__name__, attr)) + else: + msg = "%(me)s.%(attr)s is deprecated; use %(me)s.socket.%(attr)s " \ + "instead" % {'me' : self.__class__.__name__, 'attr' : attr} + warnings.warn(msg, DeprecationWarning, stacklevel=2) + return retattr + + # log and log_info may be overridden to provide more sophisticated + # logging and warning methods. In general, log is for 'hit' logging + # and 'log_info' is for informational, warning and error logging. + + def log(self, message): + sys.stderr.write('log: %s\n' % str(message)) + + def log_info(self, message, type='info'): + if type not in self.ignore_log_types: + print('%s: %s' % (type, message)) + + def handle_read_event(self): + if self.accepting: + # accepting sockets are never connected, they "spawn" new + # sockets that are connected + self.handle_accept() + elif not self.connected: + if self.connecting: + self.handle_connect_event() + self.handle_read() + else: + self.handle_read() + + def handle_connect_event(self): + err = self.socket.getsockopt(socket.SOL_SOCKET, socket.SO_ERROR) + if err != 0: + raise socket.error(err, _strerror(err)) + self.handle_connect() + self.connected = True + self.connecting = False + + def handle_write_event(self): + if self.accepting: + # Accepting sockets shouldn't get a write event. + # We will pretend it didn't happen. + return + + if not self.connected: + if self.connecting: + self.handle_connect_event() + self.handle_write() + + def handle_expt_event(self): + # handle_expt_event() is called if there might be an error on the + # socket, or if there is OOB data + # check for the error condition first + err = self.socket.getsockopt(socket.SOL_SOCKET, socket.SO_ERROR) + if err != 0: + # we can get here when select.select() says that there is an + # exceptional condition on the socket + # since there is an error, we'll go ahead and close the socket + # like we would in a subclassed handle_read() that received no + # data + self.handle_close() + else: + self.handle_expt() + + def handle_error(self): + nil, t, v, tbinfo = compact_traceback() + + # sometimes a user repr method will crash. + try: + self_repr = repr(self) + except: + self_repr = '<__repr__(self) failed for object at %0x>' % id(self) + + self.log_info( + 'uncaptured python exception, closing channel %s (%s:%s %s)' % ( + self_repr, + t, + v, + tbinfo + ), + 'error' + ) + self.handle_close() + + def handle_expt(self): + self.log_info('unhandled incoming priority event', 'warning') + + def handle_read(self): + self.log_info('unhandled read event', 'warning') + + def handle_write(self): + self.log_info('unhandled write event', 'warning') + + def handle_connect(self): + self.log_info('unhandled connect event', 'warning') + + def handle_accept(self): + pair = self.accept() + if pair is not None: + self.handle_accepted(*pair) + + def handle_accepted(self, sock, addr): + sock.close() + self.log_info('unhandled accepted event', 'warning') + + def handle_close(self): + self.log_info('unhandled close event', 'warning') + self.close() + +# --------------------------------------------------------------------------- +# adds simple buffered output capability, useful for simple clients. +# [for more sophisticated usage use asynchat.async_chat] +# --------------------------------------------------------------------------- + +class dispatcher_with_send(dispatcher): + + def __init__(self, sock=None, map=None): + dispatcher.__init__(self, sock, map) + self.out_buffer = b'' + + def initiate_send(self): + num_sent = 0 + num_sent = dispatcher.send(self, self.out_buffer[:512]) + self.out_buffer = self.out_buffer[num_sent:] + + def handle_write(self): + self.initiate_send() + + def writable(self): + return (not self.connected) or len(self.out_buffer) + + def send(self, data): + if self.debug: + self.log_info('sending %s' % repr(data)) + self.out_buffer = self.out_buffer + data + self.initiate_send() + +# --------------------------------------------------------------------------- +# used for debugging. +# --------------------------------------------------------------------------- + +def compact_traceback(): + t, v, tb = sys.exc_info() + tbinfo = [] + if not tb: # Must have a traceback + raise AssertionError("traceback does not exist") + while tb: + tbinfo.append(( + tb.tb_frame.f_code.co_filename, + tb.tb_frame.f_code.co_name, + str(tb.tb_lineno) + )) + tb = tb.tb_next + + # just to be safe + del tb + + file, function, line = tbinfo[-1] + info = ' '.join(['[%s|%s|%s]' % x for x in tbinfo]) + return (file, function, line), t, v, info + +def close_all(map=None, ignore_all=False): + if map is None: + map = socket_map + for x in list(map.values()): + try: + x.close() + except OSError as x: + if x.args[0] == EBADF: + pass + elif not ignore_all: + raise + except _reraised_exceptions: + raise + except: + if not ignore_all: + raise + map.clear() + +# Asynchronous File I/O: +# +# After a little research (reading man pages on various unixen, and +# digging through the linux kernel), I've determined that select() +# isn't meant for doing asynchronous file i/o. +# Heartening, though - reading linux/mm/filemap.c shows that linux +# supports asynchronous read-ahead. So _MOST_ of the time, the data +# will be sitting in memory for us already when we go to read it. +# +# What other OS's (besides NT) support async file i/o? [VMS?] +# +# Regardless, this is useful for pipes, and stdin/stdout... + +if os.name == 'posix': + import fcntl + + class file_wrapper: + # Here we override just enough to make a file + # look like a socket for the purposes of asyncore. + # The passed fd is automatically os.dup()'d + + def __init__(self, fd): + self.fd = os.dup(fd) + + def recv(self, *args): + return os.read(self.fd, *args) + + def send(self, *args): + return os.write(self.fd, *args) + + def getsockopt(self, level, optname, buflen=None): + if (level == socket.SOL_SOCKET and + optname == socket.SO_ERROR and + not buflen): + return 0 + raise NotImplementedError("Only asyncore specific behaviour " + "implemented.") + + read = recv + write = send + + def close(self): + os.close(self.fd) + + def fileno(self): + return self.fd + + class file_dispatcher(dispatcher): + + def __init__(self, fd, map=None): + dispatcher.__init__(self, None, map) + self.connected = True + try: + fd = fd.fileno() + except AttributeError: + pass + self.set_file(fd) + # set it to non-blocking mode + flags = fcntl.fcntl(fd, fcntl.F_GETFL, 0) + flags = flags | os.O_NONBLOCK + fcntl.fcntl(fd, fcntl.F_SETFL, flags) + + def set_file(self, fd): + self.socket = file_wrapper(fd) + self._fileno = self.socket.fileno() + self.add_channel() diff --git a/src/network/http-old.py b/src/network/http-old.py new file mode 100644 index 00000000..56d24915 --- /dev/null +++ b/src/network/http-old.py @@ -0,0 +1,49 @@ +import asyncore +import socket +import time + +requestCount = 0 +parallel = 50 +duration = 60 + + +class HTTPClient(asyncore.dispatcher): + port = 12345 + + def __init__(self, host, path, connect=True): + if not hasattr(self, '_map'): + asyncore.dispatcher.__init__(self) + if connect: + self.create_socket(socket.AF_INET, socket.SOCK_STREAM) + self.connect((host, HTTPClient.port)) + self.buffer = 'GET %s HTTP/1.0\r\n\r\n' % path + + def handle_close(self): + global requestCount + requestCount += 1 + self.close() + + def handle_read(self): +# print self.recv(8192) + self.recv(8192) + + def writable(self): + return (len(self.buffer) > 0) + + def handle_write(self): + sent = self.send(self.buffer) + self.buffer = self.buffer[sent:] + +if __name__ == "__main__": + # initial fill + for i in range(parallel): + HTTPClient('127.0.0.1', '/') + start = time.time() + while (time.time() - start < duration): + if (len(asyncore.socket_map) < parallel): + for i in range(parallel - len(asyncore.socket_map)): + HTTPClient('127.0.0.1', '/') + print "Active connections: %i" % (len(asyncore.socket_map)) + asyncore.loop(count=len(asyncore.socket_map)/2) + if requestCount % 100 == 0: + print "Processed %i total messages" % (requestCount) diff --git a/src/network/http.py b/src/network/http.py index 56d24915..184213b0 100644 --- a/src/network/http.py +++ b/src/network/http.py @@ -1,49 +1,86 @@ -import asyncore import socket -import time -requestCount = 0 -parallel = 50 -duration = 60 +from advanceddispatcher import AdvancedDispatcher +import asyncore_pollchoose as asyncore +from proxy import Proxy, ProxyError, GeneralProxyError +from socks5 import Socks5Connection, Socks5Resolver, Socks5AuthError, Socks5Error +from socks4a import Socks4aConnection, Socks4aResolver, Socks4aError + +class HttpError(ProxyError): pass -class HTTPClient(asyncore.dispatcher): - port = 12345 +class HttpConnection(AdvancedDispatcher): + def __init__(self, host, path="/"): + AdvancedDispatcher.__init__(self) + self.path = path + self.destination = (host, 80) + self.create_socket(socket.AF_INET, socket.SOCK_STREAM) + self.connect(self.destination) + print "connecting in background to %s:%i" % (self.destination[0], self.destination[1]) - def __init__(self, host, path, connect=True): - if not hasattr(self, '_map'): - asyncore.dispatcher.__init__(self) - if connect: - self.create_socket(socket.AF_INET, socket.SOCK_STREAM) - self.connect((host, HTTPClient.port)) - self.buffer = 'GET %s HTTP/1.0\r\n\r\n' % path + def state_init(self): + self.write_buf += "GET %s HTTP/1.1\r\nHost: %s\r\nConnection: close\r\n\r\n" % (self.path, self.destination[0]) + print "\"%s\"" % (self.write_buf) + self.set_state("http_request_sent", 0) + return False - def handle_close(self): - global requestCount - requestCount += 1 - self.close() + def state_http_request_sent(self): + if len(self.read_buf) > 0: + print self.read_buf + self.read_buf = b"" + if not self.connected: + self.set_state("close", 0) + return False - def handle_read(self): -# print self.recv(8192) - self.recv(8192) - def writable(self): - return (len(self.buffer) > 0) +class Socks5HttpConnection(Socks5Connection, HttpConnection): + def __init__(self, host, path="/"): + self.path = path + Socks5Connection.__init__(self, address=(host, 80)) + + def state_socks_handshake_done(self): + HttpConnection.state_init(self) + return False + + +class Socks4aHttpConnection(Socks4aConnection, HttpConnection): + def __init__(self, host, path="/"): + Socks4aConnection.__init__(self, address=(host, 80)) + self.path = path + + def state_socks_handshake_done(self): + HttpConnection.state_init(self) + return False - def handle_write(self): - sent = self.send(self.buffer) - self.buffer = self.buffer[sent:] if __name__ == "__main__": # initial fill - for i in range(parallel): - HTTPClient('127.0.0.1', '/') - start = time.time() - while (time.time() - start < duration): - if (len(asyncore.socket_map) < parallel): - for i in range(parallel - len(asyncore.socket_map)): - HTTPClient('127.0.0.1', '/') - print "Active connections: %i" % (len(asyncore.socket_map)) - asyncore.loop(count=len(asyncore.socket_map)/2) - if requestCount % 100 == 0: - print "Processed %i total messages" % (requestCount) + + for host in ("bootstrap8080.bitmessage.org", "bootstrap8444.bitmessage.org"): + proxy = Socks5Resolver(host=host) + while len(asyncore.socket_map) > 0: + print "loop %s, len %i" % (proxy.state, len(asyncore.socket_map)) + asyncore.loop(timeout=1, count=1) + proxy.resolved() + + proxy = Socks4aResolver(host=host) + while len(asyncore.socket_map) > 0: + print "loop %s, len %i" % (proxy.state, len(asyncore.socket_map)) + asyncore.loop(timeout=1, count=1) + proxy.resolved() + + for host in ("bitmessage.org",): + direct = HttpConnection(host) + while len(asyncore.socket_map) > 0: +# print "loop, state = %s" % (direct.state) + asyncore.loop(timeout=1, count=1) + + proxy = Socks5HttpConnection(host) + while len(asyncore.socket_map) > 0: +# print "loop, state = %s" % (proxy.state) + asyncore.loop(timeout=1, count=1) + + proxy = Socks4aHttpConnection(host) + while len(asyncore.socket_map) > 0: +# print "loop, state = %s" % (proxy.state) + asyncore.loop(timeout=1, count=1) diff --git a/src/network/proxy.py b/src/network/proxy.py index d9830431..e3b5acee 100644 --- a/src/network/proxy.py +++ b/src/network/proxy.py @@ -1,15 +1,15 @@ -# SOCKS5 only - -import asyncore import socket -import struct from advanceddispatcher import AdvancedDispatcher +import asyncore_pollchoose as asyncore + +class ProxyError(Exception): pass +class GeneralProxyError(ProxyError): pass class Proxy(AdvancedDispatcher): # these are global, and if you change config during runtime, all active/new # instances should change too - _proxy = ["", 1080] + _proxy = ("127.0.0.1", 9050) _auth = None _remote_dns = True @@ -19,8 +19,8 @@ class Proxy(AdvancedDispatcher): @proxy.setter def proxy(self, address): - if (not type(address) in (list,tuple)) or (len(address) < 2) or (type(address[0]) != type('')) or (type(address[1]) != int): - raise + if type(address) != tuple or (len(address) < 2) or (type(str(address[0])) != type('')) or (type(address[1]) != int): + raise ValueError self.__class__._proxy = address @property @@ -31,160 +31,11 @@ class Proxy(AdvancedDispatcher): def auth(self, authTuple): self.__class__._auth = authTuple - def __init__(self, address=None): - if (not type(address) in (list,tuple)) or (len(address) < 2) or (type(address[0]) != type('')) or (type(address[1]) != int): - raise - AdvancedDispatcher.__init__(self, self.sock) + def __init__(self, address): + if type(address) != tuple or (len(address) < 2) or (type(str(address[0])) != type('')) or (type(address[1]) != int): + raise ValueError + AdvancedDispatcher.__init__(self) self.destination = address self.create_socket(socket.AF_INET, socket.SOCK_STREAM) - self.sslSocket.setblocking(0) self.connect(self.proxy) - - -class SOCKS5(Proxy): - def __init__(self, address=None, sock=None): - Proxy.__init__(self, address) - self.state = "init" - - def handle_connect(self): - self.process() - - def state_init(self): - if self._auth: - self.write_buf += struct.pack('BBBB', 0x05, 0x02, 0x00, 0x02) - else: - self.write_buf += struct.pack('BBB', 0x05, 0x01, 0x00) - self.set_state("auth_1", 0) - - def state_auth_1(self): - if not self.read_buf_sufficient(2): - return False - ret = struct.unpack('BB', self.read_buf) - self.read_buf = self.read_buf[2:] - if ret[0] != 5: - # general error - raise - elif ret[1] == 0: - # no auth required - self.set_state("auth_done", 2) - elif ret[1] == 2: - # username/password - self.write_buf += struct.pack('BB', 1, len(self._auth[0])) + \ - self._auth[0] + struct.pack('B', len(self._auth[1])) + \ - self._auth[1] - self.set_state("auth_1", 2) - else: - if ret[1] == 0xff: - # auth error - raise - else: - # other error - raise - - def state_auth_needed(self): - if not self.read_buf_sufficient(2): - return False - ret = struct.unpack('BB', self.read_buf) - if ret[0] != 1: - # general error - raise - if ret[1] != 0: - # auth error - raise - # all ok - self.set_state = ("auth_done", 2) - - def state_pre_connect(self): - if not self.read_buf_sufficient(4): - return False - # Get the response - if self.read_buf[0:1] != chr(0x05).encode(): - # general error - self.close() - raise - elif self.read_buf[1:2] != chr(0x00).encode(): - # Connection failed - self.close() - if ord(self.read_buf[1:2])<=8: - # socks 5 erro - raise - #raise Socks5Error((ord(resp[1:2]), _socks5errors[ord(resp[1:2])])) - else: - raise - #raise Socks5Error((9, _socks5errors[9])) - # Get the bound address/port - elif self.read_buf[3:4] == chr(0x01).encode(): - self.set_state("proxy_addr_1", 4) - elif self.read_buf[3:4] == chr(0x03).encode(): - self.set_state("proxy_addr_2_1", 4) - else: - self.close() - raise GeneralProxyError((1,_generalerrors[1])) - - def state_proxy_addr_1(self): - if not self.read_buf_sufficient(4): - return False - self.boundaddr = self.read_buf[0:4] - self.set_state("proxy_port", 4) - - def state_proxy_addr_2_1(self): - if not self.read_buf_sufficient(1): - return False - self.address_length = ord(self.read_buf[0:1]) - self.set_state("proxy_addr_2_2", 1) - - def state_proxy_addr_2_2(self): - if not self.read_buf_sufficient(self.address_length): - return False - self.boundaddr = read_buf - self.set_state("proxy_port", self.address_length) - - def state_proxy_port(self): - if not self.read_buf_sufficient(2): - return False - self.boundport = struct.unpack(">H", self.read_buf[0:2])[0] - self.__proxysockname = (self.boundaddr, self.boundport) - if self.ipaddr != None: - self.__proxypeername = (socket.inet_ntoa(self.ipaddr), self.destination[1]) - else: - self.__proxypeername = (self.destination[1], destport) - - -class SOCKS5Connection(SOCKS5): - def __init__(self, address): - SOCKS5.__init__(self, address) - - def state_auth_done(self): - # Now we can request the actual connection - self.write_buf += struct.pack('BBB', 0x05, 0x01, 0x00) - # If the given destination address is an IP address, we'll - # use the IPv4 address request even if remote resolving was specified. - try: - self.ipaddr = socket.inet_aton(self.destination[0]) - self.write_buf += chr(0x01).encode() + ipaddr - except socket.error: - # Well it's not an IP number, so it's probably a DNS name. - if Proxy._remote_dns: - # Resolve remotely - self.ipaddr = None - self.write_buf += chr(0x03).encode() + chr(len(self.destination[0])).encode() + self.destination[0] - else: - # Resolve locally - self.ipaddr = socket.inet_aton(socket.gethostbyname(self.destination[0])) - self.write_buf += chr(0x01).encode() + ipaddr - self.write_buf += struct.pack(">H", self.destination[1]) - self.set_state = ("pre_connect", 0) - - -class SOCKS5Resolver(SOCKS5): - def __init__(self, host): - self.host = host - self.port = 8444 - SOCKS5.__init__(self, [self.host, self.port]) - - def state_auth_done(self): - # Now we can request the actual connection - self.write_buf += struct.pack('BBB', 0x05, 0xF0, 0x00) - self.write_buf += chr(0x03).encode() + chr(len(self.host)).encode() + self.host - self.write_buf += struct.pack(">H", self.port) - self.state = "pre_connect" + print "connecting in background to %s:%i" % (self.proxy[0], self.proxy[1]) diff --git a/src/network/socks4a.py b/src/network/socks4a.py new file mode 100644 index 00000000..091e09a5 --- /dev/null +++ b/src/network/socks4a.py @@ -0,0 +1,104 @@ +import socket +import struct + +from advanceddispatcher import AdvancedDispatcher +import asyncore_pollchoose as asyncore +from proxy import Proxy, ProxyError, GeneralProxyError + +class Socks4aError(ProxyError): pass + + +class Socks4a(Proxy): + def __init__(self, address=None): + Proxy.__init__(self, address) + self.ipaddr = None + self.destport = address[1] + + def state_init(self): + self.set_state("auth_done", 0) + + def state_pre_connect(self): + if not self.read_buf_sufficient(8): + return False + # Get the response + if self.read_buf[0:1] != chr(0x00).encode(): + # bad data + self.close() + raise Socks4aError + elif self.read_buf[1:2] != chr(0x5A).encode(): + # Connection failed + self.close() + if ord(self.read_buf[1:2]) in (91, 92, 93): + # socks 4 erro + raise Socks4aError + #raise Socks5Error((ord(resp[1:2]), _socks5errors[ord(resp[1:2])-90])) + else: + raise Socks4aError + #raise Socks4aError((94, _socks4aerrors[4])) + # Get the bound address/port + self.boundport = struct.unpack(">H", self.read_buf[2:4])[0] + self.boundaddr = self.read_buf[4:] + self.__proxysockname = (self.boundaddr, self.boundport) + if self.ipaddr != None: + self.__proxypeername = (socket.inet_ntoa(self.ipaddr), self.destination[1]) + else: + self.__proxypeername = (self.destination[0], self.destport) + self.set_state("socks_handshake_done", 8) + + def proxy_sock_name(self): + return socket.inet_ntoa(self.__proxysockname[0]) + + def state_socks_handshake_done(self): + return False + + +class Socks4aConnection(Socks4a): + def __init__(self, address): + Socks4a.__init__(self, address=address) + + def state_auth_done(self): + # Now we can request the actual connection + rmtrslv = False + self.write_buf += struct.pack('>BBH', 0x04, 0x01, self.destination[1]) + # If the given destination address is an IP address, we'll + # use the IPv4 address request even if remote resolving was specified. + try: + self.ipaddr = socket.inet_aton(self.destination[0]) + self.write_buf += ipaddr + except socket.error: + # Well it's not an IP number, so it's probably a DNS name. + if Proxy._remote_dns: + # Resolve remotely + rmtrslv = True + self.ipaddr = None + self.write_buf += struct.pack("BBBB", 0x00, 0x00, 0x00, 0x01) + else: + # Resolve locally + self.ipaddr = socket.inet_aton(socket.gethostbyname(self.destination[0])) + self.write_buf += self.ipaddr + if self._auth: + self.write_buf += self._auth[0] + self.write_buf += chr(0x00).encode() + if rmtrslv: + self.write_buf += self.destination[0] + chr(0x00).encode() + self.set_state("pre_connect", 0) + + +class Socks4aResolver(Socks4a): + def __init__(self, host): + self.host = host + self.port = 8444 + Socks4a.__init__(self, address=(self.host, self.port)) + + def state_auth_done(self): + # Now we can request the actual connection + self.write_buf += struct.pack('>BBH', 0x04, 0xF0, self.destination[1]) + self.write_buf += struct.pack("BBBB", 0x00, 0x00, 0x00, 0x01) + if self._auth: + self.write_buf += self._auth[0] + self.write_buf += chr(0x00).encode() + self.write_buf += self.host + chr(0x00).encode() + self.set_state("pre_connect", 0) + + def resolved(self): + print "Resolved %s as %s" % (self.host, self.proxy_sock_name()) diff --git a/src/network/socks5.py b/src/network/socks5.py new file mode 100644 index 00000000..5f3164ca --- /dev/null +++ b/src/network/socks5.py @@ -0,0 +1,170 @@ +import socket +import struct + +from advanceddispatcher import AdvancedDispatcher +import asyncore_pollchoose as asyncore +from proxy import Proxy, ProxyError, GeneralProxyError + +class Socks5AuthError(ProxyError): pass +class Socks5Error(ProxyError): pass + + +class Socks5(Proxy): + def __init__(self, address=None): + Proxy.__init__(self, address) + self.ipaddr = None + self.destport = address[1] + + def handle_connect(self): + self.process() + + def state_init(self): + if self._auth: + self.write_buf += struct.pack('BBBB', 0x05, 0x02, 0x00, 0x02) + else: + self.write_buf += struct.pack('BBB', 0x05, 0x01, 0x00) + self.set_state("auth_1", 0) + + def state_auth_1(self): + if not self.read_buf_sufficient(2): + return False + ret = struct.unpack('BB', self.read_buf) + self.read_buf = self.read_buf[2:] + if ret[0] != 5: + # general error + raise GeneralProxyError + elif ret[1] == 0: + # no auth required + self.set_state("auth_done", 2) + elif ret[1] == 2: + # username/password + self.write_buf += struct.pack('BB', 1, len(self._auth[0])) + \ + self._auth[0] + struct.pack('B', len(self._auth[1])) + \ + self._auth[1] + self.set_state("auth_1", 2) + else: + if ret[1] == 0xff: + # auth error + raise Socks5AuthError + else: + # other error + raise Socks5Error + + def state_auth_needed(self): + if not self.read_buf_sufficient(2): + return False + ret = struct.unpack('BB', self.read_buf) + if ret[0] != 1: + # general error + raise Socks5Error + if ret[1] != 0: + # auth error + raise Socks5AuthError + # all ok + self.set_state = ("auth_done", 2) + + def state_pre_connect(self): + if not self.read_buf_sufficient(4): + return False + # Get the response + if self.read_buf[0:1] != chr(0x05).encode(): + # general error + self.close() + raise Socks5Error + elif self.read_buf[1:2] != chr(0x00).encode(): + # Connection failed + self.close() + if ord(self.read_buf[1:2])<=8: + # socks 5 erro + raise Socks5Error + #raise Socks5Error((ord(resp[1:2]), _socks5errors[ord(resp[1:2])])) + else: + raise Socks5Error + #raise Socks5Error((9, _socks5errors[9])) + # Get the bound address/port + elif self.read_buf[3:4] == chr(0x01).encode(): + self.set_state("proxy_addr_1", 4) + elif self.read_buf[3:4] == chr(0x03).encode(): + self.set_state("proxy_addr_2_1", 4) + else: + self.close() + #raise GeneralProxyError((1,_generalerrors[1])) + raise GeneralProxyError + + def state_proxy_addr_1(self): + if not self.read_buf_sufficient(4): + return False + self.boundaddr = self.read_buf[0:4] + self.set_state("proxy_port", 4) + + def state_proxy_addr_2_1(self): + if not self.read_buf_sufficient(1): + return False + self.address_length = ord(self.read_buf[0:1]) + self.set_state("proxy_addr_2_2", 1) + + def state_proxy_addr_2_2(self): + if not self.read_buf_sufficient(self.address_length): + return False + self.boundaddr = read_buf + self.set_state("proxy_port", self.address_length) + + def state_proxy_port(self): + if not self.read_buf_sufficient(2): + return False + self.boundport = struct.unpack(">H", self.read_buf[0:2])[0] + self.__proxysockname = (self.boundaddr, self.boundport) + if self.ipaddr != None: + self.__proxypeername = (socket.inet_ntoa(self.ipaddr), self.destination[1]) + else: + self.__proxypeername = (self.destination[0], self.destport) + self.set_state("socks_handshake_done", 2) + + def proxy_sock_name(self): + return socket.inet_ntoa(self.__proxysockname[0]) + + def state_socks_handshake_done(self): + return False + + +class Socks5Connection(Socks5): + def __init__(self, address): + Socks5.__init__(self, address=address) + + def state_auth_done(self): + # Now we can request the actual connection + self.write_buf += struct.pack('BBB', 0x05, 0x01, 0x00) + # If the given destination address is an IP address, we'll + # use the IPv4 address request even if remote resolving was specified. + try: + self.ipaddr = socket.inet_aton(self.destination[0]) + self.write_buf += chr(0x01).encode() + self.ipaddr + except socket.error: + # Well it's not an IP number, so it's probably a DNS name. + if Proxy._remote_dns: + # Resolve remotely + self.ipaddr = None + self.write_buf += chr(0x03).encode() + chr(len(self.destination[0])).encode() + self.destination[0] + else: + # Resolve locally + self.ipaddr = socket.inet_aton(socket.gethostbyname(self.destination[0])) + self.write_buf += chr(0x01).encode() + self.ipaddr + self.write_buf += struct.pack(">H", self.destination[1]) + self.set_state("pre_connect", 0) + + +class Socks5Resolver(Socks5): + def __init__(self, host): + self.host = host + self.port = 8444 + Socks5.__init__(self, address=(self.host, self.port)) + + def state_auth_done(self): + # Now we can request the actual connection + self.write_buf += struct.pack('BBB', 0x05, 0xF0, 0x00) + self.write_buf += chr(0x03).encode() + chr(len(self.host)).encode() + str(self.host) + self.write_buf += struct.pack(">H", self.port) + self.set_state("pre_connect", 0) + + def resolved(self): + print "Resolved %s as %s" % (self.host, self.proxy_sock_name()) From a1d1114cb26fb9038eea38f7b41a2b7f7f5ce285 Mon Sep 17 00:00:00 2001 From: Peter Surda Date: Fri, 10 Mar 2017 23:56:38 +0100 Subject: [PATCH 019/407] New network subsystem updates - auto-select select/poll/epoll/kqueue depending on what's available --- src/network/asyncore_pollchoose.py | 43 ++++++++++++++++++++++++------ src/network/http.py | 4 +-- 2 files changed, 37 insertions(+), 10 deletions(-) diff --git a/src/network/asyncore_pollchoose.py b/src/network/asyncore_pollchoose.py index 19ec9f42..fc562671 100644 --- a/src/network/asyncore_pollchoose.py +++ b/src/network/asyncore_pollchoose.py @@ -172,7 +172,10 @@ def poll_poller(timeout=0.0, map=None): if timeout is not None: # timeout is in milliseconds timeout = int(timeout*1000) - pollster = select.poll() + try: + poll_poller.pollster + except AttributeError: + poll_poller.pollster = select.poll() if map: for fd, obj in list(map.items()): flags = 0 @@ -182,9 +185,12 @@ def poll_poller(timeout=0.0, map=None): if obj.writable() and not obj.accepting: flags |= select.POLLOUT if flags: - pollster.register(fd, flags) + try: + poll_poller.pollster.modify(fd, flags) + except IOError: + poll_poller.pollster.register(fd, flags) try: - r = pollster.poll(timeout) + r = poll_poller.pollster.poll(timeout) except KeyboardInterrupt: r = [] for fd, flags in r: @@ -201,7 +207,10 @@ def epoll_poller(timeout=0.0, map=None): """A poller which uses epoll(), supported on Linux 2.5.44 and newer.""" if map is None: map = socket_map - pollster = select.epoll() + try: + epoll_poller.pollster + except AttributeError: + epoll_poller.pollster = select.epoll() if map: for fd, obj in map.items(): flags = 0 @@ -213,9 +222,12 @@ def epoll_poller(timeout=0.0, map=None): # Only check for exceptions if object was either readable # or writable. flags |= select.POLLERR | select.POLLHUP | select.POLLNVAL - pollster.register(fd, flags) + try: + epoll_poller.pollster.register(fd, flags) + except IOError: + epoll_poller.pollster.modify(fd, flags) try: - r = pollster.poll(timeout) + r = epoll_poller.pollster.poll(timeout) except select.error, err: if err.args[0] != EINTR: raise @@ -265,9 +277,14 @@ def loop(timeout=30.0, use_poll=False, map=None, count=None, # code which grants backward compatibility with "use_poll" # argument which should no longer be used in favor of # "poller" - if use_poll and hasattr(select, 'poll'): + + if hasattr(select, 'epoll'): + poller = epoll_poller + elif hasattr(select, 'kqueue'): + poller = kqueue_poller + elif hasattr(select, 'poll'): poller = poll_poller - else: + elif hasattr(select, 'select'): poller = select_poller if count is None: @@ -349,6 +366,16 @@ class dispatcher: #self.log_info('closing channel %d:%s' % (fd, self)) del map[fd] self._fileno = None + try: + epoll_poller.pollster.unregister(fd) + except (AttributeError, KeyError): + # no epoll used, or not registered + pass + try: + poll_poller.pollster.unregister(fd) + except (AttributeError, KeyError): + # no poll used, or not registered + pass def create_socket(self, family=socket.AF_INET, type=socket.SOCK_STREAM): self.family_and_type = family, type diff --git a/src/network/http.py b/src/network/http.py index 184213b0..93828c83 100644 --- a/src/network/http.py +++ b/src/network/http.py @@ -20,13 +20,13 @@ class HttpConnection(AdvancedDispatcher): def state_init(self): self.write_buf += "GET %s HTTP/1.1\r\nHost: %s\r\nConnection: close\r\n\r\n" % (self.path, self.destination[0]) - print "\"%s\"" % (self.write_buf) + print "Sending %ib" % (len(self.write_buf)) self.set_state("http_request_sent", 0) return False def state_http_request_sent(self): if len(self.read_buf) > 0: - print self.read_buf + print "Received %ib" % (len(self.read_buf)) self.read_buf = b"" if not self.connected: self.set_state("close", 0) From 49869d0b567d7deb05f3cc298ec0ce804fa6eb3f Mon Sep 17 00:00:00 2001 From: Peter Surda Date: Sat, 11 Mar 2017 11:12:08 +0100 Subject: [PATCH 020/407] Networking subsystem updates - version command struct for faster unpacking - increase read buffer to 2MB to allow a full command to fit - initial bitmessage protocol class (WIP) - error handling - remove duplicate method --- src/bmproto.py | 141 +++++++++++++++++++++++++++++ src/network/advanceddispatcher.py | 4 +- src/network/asyncore_pollchoose.py | 4 +- src/network/socks5.py | 3 - src/protocol.py | 2 + 5 files changed, 146 insertions(+), 8 deletions(-) create mode 100644 src/bmproto.py diff --git a/src/bmproto.py b/src/bmproto.py new file mode 100644 index 00000000..8bd90aff --- /dev/null +++ b/src/bmproto.py @@ -0,0 +1,141 @@ +import hashlib +import time +import socket + +from network.advanceddispatcher import AdvancedDispatcher +import network.asyncore_pollchoose as asyncore +from network.proxy import Proxy, ProxyError, GeneralProxyError +from network.socks5 import Socks5Connection, Socks5Resolver, Socks5AuthError, Socks5Error +from network.socks4a import Socks4aConnection, Socks4aResolver, Socks4aError + +import addresses +import protocol + +class BMProtoError(ProxyError): pass + + +class BMConnection(AdvancedDispatcher): + def __init__(self, address): + AdvancedDispatcher.__init__(self) + self.destination = address + self.payload = None + self.create_socket(socket.AF_INET, socket.SOCK_STREAM) + self.connect(self.destination) + print "connecting in background to %s:%i" % (self.destination[0], self.destination[1]) + + def bm_proto_len_sufficient(self): + if len(self.read_buf) < protocol.Header.size: + print "Length below header size" + return False + if not self.payload: + self.magic, self.command, self.payloadLength, self.checksum = protocol.Header.unpack(self.read_buf[:protocol.Header.size]) + self.command = self.command.rstrip('\x00') + if self.payloadLength > 1600100: # ~1.6 MB which is the maximum possible size of an inv message. + return False + if len(self.read_buf) < self.payloadLength + protocol.Header.size: + print "Length below announced object length" + return False + self.payload = self.read_buf[protocol.Header.size:self.payloadLength + protocol.Header.size] + return True + + def bm_check_command(self): + if self.magic != 0xE9BEB4D9: + self.set_state("crap", protocol.Header.size) + print "Bad magic" + self.payload = None + return False + if self.checksum != hashlib.sha512(self.payload).digest()[0:4]: + self.set_state("crap", protocol.Header.size) + self.payload = None + print "Bad checksum" + return False + print "received %s (%ib)" % (self.command, self.payloadLength) + return True + + def state_init(self): + self.write_buf += protocol.assembleVersionMessage(self.destination[0], self.destination[1], (1,), False) + if True: + print "Sending version (%ib)" % len(self.write_buf) + self.set_state("bm_reccommand", 0) + return False + + def state_bm_reccommand(self): + if not self.bm_proto_len_sufficient(): + return False + if not self.bm_check_command(): + return False + if self.command == "version": + self.bm_recversion() + elif self.command == "verack": + self.bm_recverack() + else: + print "unimplemented command %s" % (self.command) + self.set_state("bm_reccommand", protocol.Header.size + self.payloadLength) + self.payload = None + print "buffer length = %i" % (len(self.read_buf)) + return False + + def bm_recverack(self): + self.verackReceived = True + return False + + def bm_recversion(self): + self.remoteProtocolVersion, self.services, timestamp, padding1, self.myExternalIP, padding2, self.remoteNodeIncomingPort = protocol.VersionPacket.unpack(self.payload[:82]) + print "remoteProtocolVersion: %i" % (self.remoteProtocolVersion) + print "services: %08X" % (self.services) + print "time offset: %i" % (timestamp - int(time.time())) + print "my external IP: %s" % (socket.inet_ntoa(self.myExternalIP)) + print "remote node incoming port: %i" % (self.remoteNodeIncomingPort) + useragentLength, lengthofUseragentVarint = addresses.decodeVarint(self.payload[80:84]) + readPosition = 80 + lengthOfUseragentVarint + self.userAgent = self.payload[readPosition:readPosition + useragentLength] + readPosition += useragentLength + print "user agent: %s" % (self.userAgent) + return False + + def state_http_request_sent(self): + if len(self.read_buf) > 0: + print self.read_buf + self.read_buf = b"" + if not self.connected: + self.set_state("close", 0) + return False + + +class Socks5BMConnection(Socks5Connection, BMConnection): + def __init__(self, address): + Socks5Connection.__init__(self, address=address) + + def state_socks_handshake_done(self): + BMConnection.state_init(self) + return False + + +class Socks4aBMConnection(Socks4aConnection, BMConnection): + def __init__(self, address): + Socks4aConnection.__init__(self, address=address) + + def state_socks_handshake_done(self): + BMConnection.state_init(self) + return False + + +if __name__ == "__main__": + # initial fill + + for host in (("127.0.0.1", 8448),): + direct = BMConnection(host) + while len(asyncore.socket_map) > 0: + print "loop, state = %s" % (direct.state) + asyncore.loop(timeout=1, count=1) + continue + + proxy = Socks5BMConnection(host) + while len(asyncore.socket_map) > 0: +# print "loop, state = %s" % (proxy.state) + asyncore.loop(timeout=1, count=1) + + proxy = Socks4aBMConnection(host) + while len(asyncore.socket_map) > 0: +# print "loop, state = %s" % (proxy.state) + asyncore.loop(timeout=1, count=1) diff --git a/src/network/advanceddispatcher.py b/src/network/advanceddispatcher.py index 9ec7f496..c672f9f9 100644 --- a/src/network/advanceddispatcher.py +++ b/src/network/advanceddispatcher.py @@ -1,7 +1,7 @@ import asyncore_pollchoose as asyncore class AdvancedDispatcher(asyncore.dispatcher): - _buf_len = 131072 + _buf_len = 2097152 # 2MB def __init__(self): if not hasattr(self, '_map'): @@ -53,5 +53,3 @@ class AdvancedDispatcher(asyncore.dispatcher): def handle_connect(self): self.process() - - diff --git a/src/network/asyncore_pollchoose.py b/src/network/asyncore_pollchoose.py index fc562671..9f231e0e 100644 --- a/src/network/asyncore_pollchoose.py +++ b/src/network/asyncore_pollchoose.py @@ -368,12 +368,12 @@ class dispatcher: self._fileno = None try: epoll_poller.pollster.unregister(fd) - except (AttributeError, KeyError): + except (AttributeError, KeyError, TypeError): # no epoll used, or not registered pass try: poll_poller.pollster.unregister(fd) - except (AttributeError, KeyError): + except (AttributeError, KeyError, TypeError): # no poll used, or not registered pass diff --git a/src/network/socks5.py b/src/network/socks5.py index 5f3164ca..841c253b 100644 --- a/src/network/socks5.py +++ b/src/network/socks5.py @@ -15,9 +15,6 @@ class Socks5(Proxy): self.ipaddr = None self.destport = address[1] - def handle_connect(self): - self.process() - def state_init(self): if self._auth: self.write_buf += struct.pack('BBBB', 0x05, 0x02, 0x00, 0x02) diff --git a/src/protocol.py b/src/protocol.py index 9d66ec2f..a889ab93 100644 --- a/src/protocol.py +++ b/src/protocol.py @@ -34,6 +34,8 @@ eightBytesOfRandomDataUsedToDetectConnectionsToSelf = pack( #New code should use CreatePacket instead of Header.pack Header = Struct('!L12sL4s') +VersionPacket = Struct('>LqQ20sI36sH') + # Bitfield def getBitfield(address): From 0529fe23131b1a9ee400392a8898386be7cd362f Mon Sep 17 00:00:00 2001 From: Peter Surda Date: Sat, 11 Mar 2017 12:14:40 +0100 Subject: [PATCH 021/407] Known node count updates - if too many nodes, only delete oldest nodes in bootstrap provider mode, in normal mode ignore new nodes as it used to before - in bootstrap provider mode, penalise nodes announced by others by 1 day instead of 3 hours --- src/class_receiveDataThread.py | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/src/class_receiveDataThread.py b/src/class_receiveDataThread.py index 124e06c0..0ddd5739 100644 --- a/src/class_receiveDataThread.py +++ b/src/class_receiveDataThread.py @@ -630,17 +630,18 @@ class receiveDataThread(threading.Thread): knownnodes.knownNodes[recaddrStream] = {} peerFromAddrMessage = state.Peer(hostStandardFormat, recaddrPort) if peerFromAddrMessage not in knownnodes.knownNodes[recaddrStream]: - knownnodes.trimKnownNodes(recaddrStream) # only if recent if timeSomeoneElseReceivedMessageFromThisNode > (int(time.time()) - 10800) and timeSomeoneElseReceivedMessageFromThisNode < (int(time.time()) + 10800): - logger.debug('added new node ' + str(peerFromAddrMessage) + ' to knownNodes in stream ' + str(recaddrStream)) # bootstrap provider? if BMConfigParser().safeGetInt('bitmessagesettings', 'maxoutboundconnections') >= \ BMConfigParser().safeGetInt('bitmessagesettings', 'maxtotalconnections', 200): + knownnodes.trimKnownNodes(recaddrStream) with knownnodes.knownNodesLock: - knownnodes.knownNodes[recaddrStream][peerFromAddrMessage] = int(time.time()) - 10800 + knownnodes.knownNodes[recaddrStream][peerFromAddrMessage] = int(time.time()) - 86400 # penalise initially by 1 day + logger.debug('added new node ' + str(peerFromAddrMessage) + ' to knownNodes in stream ' + str(recaddrStream)) + shared.needToWriteKnownNodesToDisk = True # normal mode - else: + elif len(knownnodes.knownNodes[recaddrStream]) < 20000: with knownnodes.knownNodesLock: knownnodes.knownNodes[recaddrStream][peerFromAddrMessage] = timeSomeoneElseReceivedMessageFromThisNode hostDetails = ( @@ -648,7 +649,8 @@ class receiveDataThread(threading.Thread): recaddrStream, recaddrServices, hostStandardFormat, recaddrPort) protocol.broadcastToSendDataQueues(( recaddrStream, 'advertisepeer', hostDetails)) - shared.needToWriteKnownNodesToDisk = True + logger.debug('added new node ' + str(peerFromAddrMessage) + ' to knownNodes in stream ' + str(recaddrStream)) + shared.needToWriteKnownNodesToDisk = True # only update if normal mode elif BMConfigParser().safeGetInt('bitmessagesettings', 'maxoutboundconnections') < \ BMConfigParser().safeGetInt('bitmessagesettings', 'maxtotalconnections', 200): From 13223887fc782f2171c78aaaaedf04df7247833f Mon Sep 17 00:00:00 2001 From: Peter Surda Date: Fri, 17 Mar 2017 19:25:19 +0100 Subject: [PATCH 022/407] Setup.py updates - only waits for a prompt if running from a TTY - support for Ubuntu 12 and older (different msgpack package name) - fix crash for missing keys in the distro definition - accept alternative msgpack providers - add pyopencl as an extra --- setup.py | 32 +++++++++++++++++++++++++++----- 1 file changed, 27 insertions(+), 5 deletions(-) diff --git a/setup.py b/setup.py index 1a273eaf..95c7ffb5 100644 --- a/setup.py +++ b/setup.py @@ -17,6 +17,7 @@ packageManager = { "FreeBSD": "pkg install", "Debian": "apt-get install", "Ubuntu": "apt-get install", + "Ubuntu 12": "apt-get install", "openSUSE": "zypper install", "Fedora": "dnf install", "Guix": "guix package -i", @@ -29,6 +30,7 @@ packageName = { "FreeBSD": "py27-qt4", "Debian": "python-qt4", "Ubuntu": "python-qt4", + "Ubuntu 12": "python-qt4", "openSUSE": "python-qt", "Fedora": "PyQt4", "Guix": "python2-pyqt@4.11.4", @@ -44,6 +46,7 @@ packageName = { "FreeBSD": "py27-msgpack-python", "Debian": "python-msgpack", "Ubuntu": "python-msgpack", + "Ubuntu 12": "msgpack-python", "openSUSE": "python-msgpack-python", "Fedora": "python2-msgpack", "Guix": "python2-msgpack", @@ -53,6 +56,7 @@ packageName = { "FreeBSD": "py27-pyopencl", "Debian": "python-pyopencl", "Ubuntu": "python-pyopencl", + "Ubuntu 12": "python-pyopencl", "Fedora": "python2-pyopencl", "openSUSE": "", "OpenBSD": "", @@ -68,6 +72,7 @@ packageName = { "FreeBSD": "py27-setuptools", "Debian": "python-setuptools", "Ubuntu": "python-setuptools", + "Ubuntu 12": "python-setuptools", "Fedora": "python2-setuptools", "openSUSE": "python-setuptools", "Guix": "python2-setuptools", @@ -94,6 +99,7 @@ def detectOS(): detectOS.result = "Windows" elif os.path.isfile("/etc/os-release"): with open("/etc/os-release", 'rt') as osRelease: + version = None for line in osRelease: if line.startswith("NAME="): line = line.lower() @@ -109,6 +115,13 @@ def detectOS(): detectOS.result = "Gentoo" else: detectOS.result = None + if line.startswith("VERSION_ID="): + try: + version = float(line.split("\"")[1]) + except ValueError: + pass + if detectOS.result == "Ubuntu" and version < 14: + detectOS.result = "Ubuntu 12" elif os.path.isfile("/etc/config.scm"): detectOS.result = "Guix" return detectOS.result @@ -133,8 +146,11 @@ def prereqToPackages(): packageManager[detectOS()], " ".join( packageName[x][detectOS()] for x in detectPrereqs(True))) for package in detectPrereqs(True): - if packageName[package]['optional']: - print packageName[package]['description'] + try: + if packageName[package]['optional']: + print packageName[package]['description'] + except KeyError: + pass def compilerToPackages(): if not detectOS() in compiling: @@ -159,7 +175,7 @@ if __name__ == "__main__": print "It looks like you're missing setuptools." sys.exit() - if detectPrereqs(True) != []: + if detectPrereqs(True) != [] and sys.stdin.isatty(): print "Press Return to continue" try: nothing = raw_input() @@ -176,6 +192,11 @@ if __name__ == "__main__": libraries=['pthread', 'crypto'], ) + installRequires = [] + # this will silently accept alternative providers of msgpack if they are already installed + if "msgpack" in detectPrereqs(True): + installRequires.append("msgpack-python") + try: dist = setup( name='pybitmessage', @@ -190,9 +211,10 @@ if __name__ == "__main__": url='https://bitmessage.org', # TODO: add keywords #keywords='', - install_requires=['msgpack-python'], + install_requires=[installRequires], extras_require={ - 'qrcode': ['qrcode'] + 'qrcode': ['qrcode'], + 'pyopencl': ['pyopencl'] }, classifiers=[ "License :: OSI Approved :: MIT License" From 1af49a016592fa639a48225e085205f083b5e7dd Mon Sep 17 00:00:00 2001 From: Peter Surda Date: Sun, 19 Mar 2017 22:08:00 +0100 Subject: [PATCH 023/407] Download tracking refactoring - replace PendingDownload singleton dict with a Queue - total memory and CPU requirements should be reduced - get rid of somObjectsOfWhichThisRemoteNodeIsAlearedyAware. It has very little practicle effect and only uses memory --- src/bitmessageqt/__init__.py | 10 +- src/bitmessageqt/networkstatus.py | 5 +- src/class_outgoingSynSender.py | 5 +- src/class_receiveDataThread.py | 26 ++++-- src/class_sendDataThread.py | 9 +- src/class_singleListener.py | 5 +- src/inventory.py | 148 ++++++------------------------ src/shared.py | 6 +- 8 files changed, 60 insertions(+), 154 deletions(-) diff --git a/src/bitmessageqt/__init__.py b/src/bitmessageqt/__init__.py index feaf1c48..e861f767 100644 --- a/src/bitmessageqt/__init__.py +++ b/src/bitmessageqt/__init__.py @@ -77,7 +77,7 @@ from class_objectHashHolder import objectHashHolder from class_singleWorker import singleWorker from dialogs import AddAddressDialog from helper_generic import powQueueSize -from inventory import PendingDownload, PendingUpload, PendingUploadDeadlineException +from inventory import PendingDownloadQueue, PendingUpload, PendingUploadDeadlineException import knownnodes import paths from proofofwork import getPowType @@ -2751,16 +2751,16 @@ class MyForm(settingsmixin.SMainWindow): elif reply == QtGui.QMessage.Cancel: return - if PendingDownload().len() > 0: + if PendingDownloadQueue.totalSize() > 0: reply = QtGui.QMessageBox.question(self, _translate("MainWindow", "Synchronisation pending"), - _translate("MainWindow", "Bitmessage hasn't synchronised with the network, %n object(s) to be downloaded. If you quit now, it may cause delivery delays. Wait until the synchronisation finishes?", None, QtCore.QCoreApplication.CodecForTr, PendingDownload().len()), + _translate("MainWindow", "Bitmessage hasn't synchronised with the network, %n object(s) to be downloaded. If you quit now, it may cause delivery delays. Wait until the synchronisation finishes?", None, QtCore.QCoreApplication.CodecForTr, PendingDownloadQueue.totalSize()), QtGui.QMessageBox.Yes|QtGui.QMessageBox.No|QtGui.QMessageBox.Cancel, QtGui.QMessageBox.Cancel) if reply == QtGui.QMessageBox.Yes: waitForSync = True elif reply == QtGui.QMessageBox.Cancel: return else: - PendingDownload().stop() + PendingDownloadQueue.stop() if shared.statusIconColor == 'red': reply = QtGui.QMessageBox.question(self, _translate("MainWindow", "Not connected"), @@ -2788,7 +2788,7 @@ class MyForm(settingsmixin.SMainWindow): if waitForSync: self.statusBar().showMessage(_translate( "MainWindow", "Waiting for finishing synchronisation...")) - while PendingDownload().len() > 0: + while PendingDownloadQueue.totalSize() > 0: time.sleep(0.5) QtCore.QCoreApplication.processEvents(QtCore.QEventLoop.AllEvents, 1000) diff --git a/src/bitmessageqt/networkstatus.py b/src/bitmessageqt/networkstatus.py index 7506b652..b5870a6b 100644 --- a/src/bitmessageqt/networkstatus.py +++ b/src/bitmessageqt/networkstatus.py @@ -1,8 +1,9 @@ from PyQt4 import QtCore, QtGui import time import shared + from tr import _translate -from inventory import Inventory, PendingDownload, PendingUpload +from inventory import Inventory, PendingDownloadQueue, PendingUpload import l10n from retranslateui import RetranslateMixin from uisignaler import UISignaler @@ -45,7 +46,7 @@ class NetworkStatus(QtGui.QWidget, RetranslateMixin): return "%4.0f kB" % num def updateNumberOfObjectsToBeSynced(self): - self.labelSyncStatus.setText(_translate("networkstatus", "Object(s) to be synced: %n", None, QtCore.QCoreApplication.CodecForTr, PendingDownload().len() + PendingUpload().len())) + self.labelSyncStatus.setText(_translate("networkstatus", "Object(s) to be synced: %n", None, QtCore.QCoreApplication.CodecForTr, PendingDownloadQueue.totalSize() + PendingUpload().len())) def updateNumberOfMessagesProcessed(self): self.updateNumberOfObjectsToBeSynced() diff --git a/src/class_outgoingSynSender.py b/src/class_outgoingSynSender.py index c4f385c3..9b3eac14 100644 --- a/src/class_outgoingSynSender.py +++ b/src/class_outgoingSynSender.py @@ -185,12 +185,10 @@ class outgoingSynSender(threading.Thread, StoppableThread): self.sock.shutdown(socket.SHUT_RDWR) self.sock.close() return - someObjectsOfWhichThisRemoteNodeIsAlreadyAware = {} # This is not necessairly a complete list; we clear it from time to time to save memory. sendDataThreadQueue = Queue.Queue() # Used to submit information to the send data thread for this connection. sd = sendDataThread(sendDataThreadQueue) - sd.setup(self.sock, peer.host, peer.port, self.streamNumber, - someObjectsOfWhichThisRemoteNodeIsAlreadyAware) + sd.setup(self.sock, peer.host, peer.port, self.streamNumber) sd.start() rd = receiveDataThread() @@ -199,7 +197,6 @@ class outgoingSynSender(threading.Thread, StoppableThread): peer.host, peer.port, self.streamNumber, - someObjectsOfWhichThisRemoteNodeIsAlreadyAware, self.selfInitiatedConnections, sendDataThreadQueue, sd.objectHashHolderInstance) diff --git a/src/class_receiveDataThread.py b/src/class_receiveDataThread.py index 0ddd5739..df7d18b5 100644 --- a/src/class_receiveDataThread.py +++ b/src/class_receiveDataThread.py @@ -32,7 +32,7 @@ import knownnodes from debug import logger import paths import protocol -from inventory import Inventory, PendingDownload, PendingUpload +from inventory import Inventory, PendingDownloadQueue, PendingUpload import queues import state import throttle @@ -56,7 +56,6 @@ class receiveDataThread(threading.Thread): HOST, port, streamNumber, - someObjectsOfWhichThisRemoteNodeIsAlreadyAware, selfInitiatedConnections, sendDataThreadQueue, objectHashHolderInstance): @@ -79,8 +78,8 @@ class receiveDataThread(threading.Thread): self.initiatedConnection = True for stream in self.streamNumber: self.selfInitiatedConnections[stream][self] = 0 - self.someObjectsOfWhichThisRemoteNodeIsAlreadyAware = someObjectsOfWhichThisRemoteNodeIsAlreadyAware self.objectHashHolderInstance = objectHashHolderInstance + self.downloadQueue = PendingDownloadQueue() self.startTime = time.time() def run(self): @@ -147,7 +146,6 @@ class receiveDataThread(threading.Thread): except Exception as err: logger.error('Could not delete ' + str(self.hostIdent) + ' from shared.connectedHostsList.' + str(err)) - PendingDownload().threadEnd() queues.UISignalQueue.put(('updateNetworkStatusTab', 'no data')) self.checkTimeOffsetNotification() logger.debug('receiveDataThread ending. ID ' + str(id(self)) + '. The size of the shared.connectedHostsList is now ' + str(len(shared.connectedHostsList))) @@ -240,10 +238,20 @@ class receiveDataThread(threading.Thread): self.data = self.data[payloadLength + protocol.Header.size:] # take this message out and then process the next message if self.data == '': # if there are no more messages + toRequest = [] try: - self.sendgetdata(PendingDownload().pull(100)) - except Queue.Full: + for i in range(self.downloadQueue.pendingSize, 100): + while True: + hashId = self.downloadQueue.get(False) + if not hashId in Inventory(): + toRequest.append(hashId) + break + # don't track download for duplicates + self.downloadQueue.task_done() + except Queue.Empty: pass + if len(toRequest) > 0: + self.sendgetdata(toRequest) self.processData() def sendpong(self, payload): @@ -407,7 +415,7 @@ class receiveDataThread(threading.Thread): bigInvList = {} for stream in self.streamNumber: for hash in Inventory().unexpired_hashes_by_stream(stream): - if hash not in self.someObjectsOfWhichThisRemoteNodeIsAlreadyAware and not self.objectHashHolderInstance.hasHash(hash): + if not self.objectHashHolderInstance.hasHash(hash): bigInvList[hash] = 0 numberOfObjectsInInvMessage = 0 payload = '' @@ -476,6 +484,7 @@ class receiveDataThread(threading.Thread): def recobject(self, data): self.messageProcessingStartTime = time.time() lengthOfTimeWeShouldUseToProcessThisMessage = shared.checkAndShareObjectWithPeers(data) + self.downloadQueue.task_done() """ Sleeping will help guarantee that we can process messages faster than a @@ -509,8 +518,7 @@ class receiveDataThread(threading.Thread): objectsNewToMe -= Inventory().hashes_by_stream(stream) logger.info('inv message lists %s objects. Of those %s are new to me. It took %s seconds to figure that out.', numberOfItemsInInv, len(objectsNewToMe), time.time()-startTime) for item in objectsNewToMe: - PendingDownload().add(item) - self.someObjectsOfWhichThisRemoteNodeIsAlreadyAware[item] = 0 # helps us keep from sending inv messages to peers that already know about the objects listed therein + self.downloadQueue.put(item) # Send a getdata message to our peer to request the object with the given # hash diff --git a/src/class_sendDataThread.py b/src/class_sendDataThread.py index ac11c6b2..792fedd0 100644 --- a/src/class_sendDataThread.py +++ b/src/class_sendDataThread.py @@ -39,8 +39,8 @@ class sendDataThread(threading.Thread): sock, HOST, PORT, - streamNumber, - someObjectsOfWhichThisRemoteNodeIsAlreadyAware): + streamNumber + ): self.sock = sock self.peer = state.Peer(HOST, PORT) self.name = "sendData-" + self.peer.host.replace(":", ".") # log parser field separator @@ -52,7 +52,6 @@ class sendDataThread(threading.Thread): 1 # This must be set using setRemoteProtocolVersion command which is sent through the self.sendDataThreadQueue queue. self.lastTimeISentData = int( time.time()) # If this value increases beyond five minutes ago, we'll send a pong message to keep the connection alive. - self.someObjectsOfWhichThisRemoteNodeIsAlreadyAware = someObjectsOfWhichThisRemoteNodeIsAlreadyAware if streamNumber == -1: # This was an incoming connection. self.initiatedConnection = False else: @@ -165,8 +164,7 @@ class sendDataThread(threading.Thread): if self.connectionIsOrWasFullyEstablished: # only send inv messages if we have send and heard a verack from the remote node payload = '' for hash in data: - if hash not in self.someObjectsOfWhichThisRemoteNodeIsAlreadyAware: - payload += hash + payload += hash if payload != '': payload = encodeVarint(len(payload)/32) + payload packet = protocol.CreatePacket('inv', payload) @@ -176,7 +174,6 @@ class sendDataThread(threading.Thread): logger.error('sendinv: self.sock.sendall failed') break elif command == 'pong': - self.someObjectsOfWhichThisRemoteNodeIsAlreadyAware.clear() # To save memory, let us clear this data structure from time to time. As its function is to help us keep from sending inv messages to peers which sent us the same inv message mere seconds earlier, it will be fine to clear this data structure from time to time. if self.lastTimeISentData < (int(time.time()) - 298): # Send out a pong message to keep the connection alive. logger.debug('Sending pong to ' + str(self.peer) + ' to keep connection alive.') diff --git a/src/class_singleListener.py b/src/class_singleListener.py index 5332929c..243a494a 100644 --- a/src/class_singleListener.py +++ b/src/class_singleListener.py @@ -146,19 +146,18 @@ class singleListener(threading.Thread, StoppableThread): else: break - someObjectsOfWhichThisRemoteNodeIsAlreadyAware = {} # This is not necessairly a complete list; we clear it from time to time to save memory. sendDataThreadQueue = Queue.Queue() # Used to submit information to the send data thread for this connection. socketObject.settimeout(20) sd = sendDataThread(sendDataThreadQueue) sd.setup( - socketObject, HOST, PORT, -1, someObjectsOfWhichThisRemoteNodeIsAlreadyAware) + socketObject, HOST, PORT, -1) sd.start() rd = receiveDataThread() rd.daemon = True # close the main program even if there are threads left rd.setup( - socketObject, HOST, PORT, -1, someObjectsOfWhichThisRemoteNodeIsAlreadyAware, self.selfInitiatedConnections, sendDataThreadQueue, sd.objectHashHolderInstance) + socketObject, HOST, PORT, -1, self.selfInitiatedConnections, sendDataThreadQueue, sd.objectHashHolderInstance) rd.start() logger.info('connected to ' + HOST + ' during INCOMING request.') diff --git a/src/inventory.py b/src/inventory.py index f2e9b37b..1982dacf 100644 --- a/src/inventory.py +++ b/src/inventory.py @@ -1,5 +1,6 @@ import collections from threading import current_thread, enumerate as threadingEnumerate, RLock +import Queue import time from helper_sql import * @@ -37,7 +38,6 @@ class Inventory(collections.MutableMapping): value = self.InventoryItem(*value) self._inventory[hash] = value self._streams[value.stream].add(hash) - PendingDownload().delete(hash) def __delitem__(self, hash): raise NotImplementedError @@ -84,131 +84,39 @@ class Inventory(collections.MutableMapping): self._streams[value.stream].add(objectHash) -@Singleton -class PendingDownload(object): +class PendingDownloadQueue(Queue.Queue): # keep a track of objects that have been advertised to us but we haven't downloaded them yet - def __init__(self): - super(self.__class__, self).__init__() - self.lock = RLock() - self.hashes = {} + def __init__(self, maxsize=0): + Queue.Queue.__init__(self, maxsize) self.stopped = False - # don't request the same object more frequently than this - self.frequency = 60 - # after requesting and not receiving an object more than this times, consider it expired - self.maxRequestCount = 3 - self.pending = {} + self.pendingSize = 0 - def add(self, objectHash): - if self.stopped: - return - with self.lock: - if objectHash not in self.hashes: - self.hashes[objectHash] = {'peers':[], 'requested':0, 'requestedCount':0} - self.hashes[objectHash]['peers'].append(current_thread().peer) + def task_done(self): + Queue.Queue.task_done(self) + if self.pendingSize > 0: + self.pendingSize -= 1 - def addPending(self, objectHash=None): - if self.stopped: - return - if current_thread().peer not in self.pending: - self.pending[current_thread().peer] = {'objects':[], 'requested':0, 'received':0} - if objectHash not in self.pending[current_thread().peer]['objects'] and not objectHash is None: - self.pending[current_thread().peer]['objects'].append(objectHash) - self.pending[current_thread().peer]['requested'] = time.time() + def get(self, block=True, timeout=None): + retval = Queue.Queue.get(self, block, timeout) + # no exception was raised + if not self.stopped: + self.pendingSize += 1 + return retval - def len(self): - with self.lock: - return sum(1 for x in self.hashes.values() if len(x) > 0) + @staticmethod + def totalSize(): + size = 0 + for thread in threadingEnumerate(): + if thread.isAlive() and hasattr(thread, 'downloadQueue'): + size += thread.downloadQueue.qsize() + thread.downloadQueue.pendingSize + return size - def pull(self, count=1): - if count < 1: - raise ValueError("Must be at least one") - objectHashes = [] - unreachableObjects = [] - if self.stopped: - return objectHashes - start = time.time() - try: - for objectHash in self.hashes.keys(): - with self.lock: - if len(objectHashes) >= count: - break - if current_thread().peer not in self.pending: - self.addPending() - if (self.pending[current_thread().peer]['requested'] >= time.time() - self.frequency or \ - self.pending[current_thread().peer]['received'] >= time.time() - self.frequency) and \ - len(self.pending[current_thread().peer]['objects']) >= count: - break - if len(self.hashes[objectHash]['peers']) == 0: - unreachableObjects.append(objectHash) - continue - # requested too long ago or not at all from any thread - if self.hashes[objectHash]['requested'] < time.time() - self.frequency: - # ready requested from this thread but haven't received yet - if objectHash in self.pending[current_thread().peer]['objects']: - # if still sending or receiving, request next - if self.pending[current_thread().peer]['received'] >= time.time() - self.frequency or \ - self.pending[current_thread().peer]['requested'] >= time.time() - self.frequency: - continue - # haven't requested or received anything recently, re-request (i.e. continue) - # the current node doesn't have the object - elif current_thread().peer not in self.hashes[objectHash]['peers']: - continue - # already requested too many times, remove all signs of this object - if self.hashes[objectHash]['requestedCount'] >= self.maxRequestCount: - del self.hashes[objectHash] - for thread in self.pending.keys(): - if objectHash in self.pending[thread]['objects']: - self.pending[thread]['objects'].remove(objectHash) - continue - # all ok, request - objectHashes.append(objectHash) - self.hashes[objectHash]['requested'] = time.time() - self.hashes[objectHash]['requestedCount'] += 1 - self.pending[current_thread().peer]['requested'] = time.time() - self.addPending(objectHash) - except (RuntimeError, KeyError, ValueError): - # the for cycle sometimes breaks if you remove elements - pass - for objectHash in unreachableObjects: - with self.lock: - if objectHash in self.hashes: - del self.hashes[objectHash] -# logger.debug("Pull took %.3f seconds", time.time() - start) - return objectHashes - - def delete(self, objectHash): - with self.lock: - if objectHash in self.hashes: - del self.hashes[objectHash] - if hasattr(current_thread(), 'peer') and current_thread().peer in self.pending: - self.pending[current_thread().peer]['received'] = time.time() - for thread in self.pending.keys(): - with self.lock: - if thread in self.pending and objectHash in self.pending[thread]['objects']: - self.pending[thread]['objects'].remove(objectHash) - - def stop(self): - with self.lock: - self.hashes = {} - self.pending = {} - - def threadEnd(self): - while True: - try: - with self.lock: - if current_thread().peer in self.pending: - for objectHash in self.pending[current_thread().peer]['objects']: - if objectHash in self.hashes: - self.hashes[objectHash]['peers'].remove(current_thread().peer) - except (KeyError): - pass - else: - break - with self.lock: - try: - del self.pending[current_thread().peer] - except KeyError: - pass + @staticmethod + def stop(): + for thread in threadingEnumerate(): + if thread.isAlive() and hasattr(thread, 'downloadQueue'): + thread.downloadQueue.stopped = True + thread.downloadQueue.pendingSize = 0 class PendingUploadDeadlineException(Exception): diff --git a/src/shared.py b/src/shared.py index c28392f6..69794033 100644 --- a/src/shared.py +++ b/src/shared.py @@ -22,7 +22,7 @@ from bmconfigparser import BMConfigParser import highlevelcrypto #import helper_startup from helper_sql import * -from inventory import Inventory, PendingDownload +from inventory import Inventory from queues import objectProcessorQueue import protocol import state @@ -342,22 +342,18 @@ def checkAndShareObjectWithPeers(data): """ if len(data) > 2 ** 18: logger.info('The payload length of this object is too large (%s bytes). Ignoring it.' % len(data)) - PendingDownload().delete(calculateInventoryHash(data)) return 0 # Let us check to make sure that the proof of work is sufficient. if not protocol.isProofOfWorkSufficient(data): logger.info('Proof of work is insufficient.') - PendingDownload().delete(calculateInventoryHash(data)) return 0 endOfLifeTime, = unpack('>Q', data[8:16]) if endOfLifeTime - int(time.time()) > 28 * 24 * 60 * 60 + 10800: # The TTL may not be larger than 28 days + 3 hours of wiggle room logger.info('This object\'s End of Life time is too far in the future. Ignoring it. Time is %s' % endOfLifeTime) - PendingDownload().delete(calculateInventoryHash(data)) return 0 if endOfLifeTime - int(time.time()) < - 3600: # The EOL time was more than an hour ago. That's too much. logger.info('This object\'s End of Life time was more than an hour ago. Ignoring the object. Time is %s' % endOfLifeTime) - PendingDownload().delete(calculateInventoryHash(data)) return 0 intObjectType, = unpack('>I', data[16:20]) try: From 9a5f7442a0555d9742e9c199438fe8f64f73dfc9 Mon Sep 17 00:00:00 2001 From: Peter Surda Date: Mon, 20 Mar 2017 00:27:11 +0100 Subject: [PATCH 024/407] Setup.py fix - handle missing "optional" key in package definition --- setup.py | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/setup.py b/setup.py index 95c7ffb5..5ea5a624 100644 --- a/setup.py +++ b/setup.py @@ -168,9 +168,12 @@ if __name__ == "__main__": "It is highly recommended to use the package manager " \ "instead of setuptools." % (detectOS()) prereqToPackages() - for module in detectPrereqs(True): - if not packageName[module]['optional']: - sys.exit() + try: + for module in detectPrereqs(True): + if not packageName[module]['optional']: + sys.exit() + except KeyError: + sys.exit() if not haveSetuptools: print "It looks like you're missing setuptools." sys.exit() From 913b401dd0a01bc4b938a1088b73dd2ab04ee7c2 Mon Sep 17 00:00:00 2001 From: Peter Surda Date: Mon, 20 Mar 2017 01:22:37 +0100 Subject: [PATCH 025/407] PendingDownloadQueue updates - track pending hashId more accurately - add timeout and a cleanup so that the download queues don't get stuck and memory is freed - randomise download order (only works for inv commands with more than 1 entry) --- src/class_receiveDataThread.py | 8 ++++---- src/class_singleCleaner.py | 5 +++++ src/inventory.py | 30 +++++++++++++++++++++++------- 3 files changed, 32 insertions(+), 11 deletions(-) diff --git a/src/class_receiveDataThread.py b/src/class_receiveDataThread.py index df7d18b5..aeb38e78 100644 --- a/src/class_receiveDataThread.py +++ b/src/class_receiveDataThread.py @@ -240,14 +240,14 @@ class receiveDataThread(threading.Thread): if self.data == '': # if there are no more messages toRequest = [] try: - for i in range(self.downloadQueue.pendingSize, 100): + for i in range(len(self.downloadQueue.pending), 100): while True: hashId = self.downloadQueue.get(False) if not hashId in Inventory(): toRequest.append(hashId) break # don't track download for duplicates - self.downloadQueue.task_done() + self.downloadQueue.task_done(hashId) except Queue.Empty: pass if len(toRequest) > 0: @@ -484,7 +484,7 @@ class receiveDataThread(threading.Thread): def recobject(self, data): self.messageProcessingStartTime = time.time() lengthOfTimeWeShouldUseToProcessThisMessage = shared.checkAndShareObjectWithPeers(data) - self.downloadQueue.task_done() + self.downloadQueue.task_done(calculateInventoryHash(data)) """ Sleeping will help guarantee that we can process messages faster than a @@ -517,7 +517,7 @@ class receiveDataThread(threading.Thread): for stream in self.streamNumber: objectsNewToMe -= Inventory().hashes_by_stream(stream) logger.info('inv message lists %s objects. Of those %s are new to me. It took %s seconds to figure that out.', numberOfItemsInInv, len(objectsNewToMe), time.time()-startTime) - for item in objectsNewToMe: + for item in random.sample(objectsNewToMe, len(objectsNewToMe)): self.downloadQueue.put(item) # Send a getdata message to our peer to request the object with the given diff --git a/src/class_singleCleaner.py b/src/class_singleCleaner.py index 921e84ed..1ed7a5e4 100644 --- a/src/class_singleCleaner.py +++ b/src/class_singleCleaner.py @@ -108,6 +108,11 @@ class singleCleaner(threading.Thread, StoppableThread): os._exit(0) shared.needToWriteKnownNodesToDisk = False + # clear download queues + for thread in threading.enumerate(): + if thread.isAlive() and hasattr(thread, 'downloadQueue'): + thread.downloadQueue.clear() + # TODO: cleanup pending upload / download if state.shutdown == 0: diff --git a/src/inventory.py b/src/inventory.py index 1982dacf..a796968a 100644 --- a/src/inventory.py +++ b/src/inventory.py @@ -86,29 +86,44 @@ class Inventory(collections.MutableMapping): class PendingDownloadQueue(Queue.Queue): # keep a track of objects that have been advertised to us but we haven't downloaded them yet + maxWait = 300 + def __init__(self, maxsize=0): Queue.Queue.__init__(self, maxsize) self.stopped = False - self.pendingSize = 0 + self.pending = {} + self.lock = RLock() - def task_done(self): + def task_done(self, hashId): Queue.Queue.task_done(self) - if self.pendingSize > 0: - self.pendingSize -= 1 + try: + with self.lock: + del self.pending[hashId] + except KeyError: + pass def get(self, block=True, timeout=None): retval = Queue.Queue.get(self, block, timeout) # no exception was raised if not self.stopped: - self.pendingSize += 1 + with self.lock: + self.pending[retval] = time.time() return retval + def clear(self): + with self.lock: + newPending = {} + for hashId in self.pending: + if self.pending[hashId] + PendingDownloadQueue.maxWait > time.time(): + newPending[hashId] = self.pending[hashId] + self.pending = newPending + @staticmethod def totalSize(): size = 0 for thread in threadingEnumerate(): if thread.isAlive() and hasattr(thread, 'downloadQueue'): - size += thread.downloadQueue.qsize() + thread.downloadQueue.pendingSize + size += thread.downloadQueue.qsize() + len(thread.downloadQueue.pending) return size @staticmethod @@ -116,7 +131,8 @@ class PendingDownloadQueue(Queue.Queue): for thread in threadingEnumerate(): if thread.isAlive() and hasattr(thread, 'downloadQueue'): thread.downloadQueue.stopped = True - thread.downloadQueue.pendingSize = 0 + with thread.downloadQueue.lock: + thread.downloadQueue.pending = {} class PendingUploadDeadlineException(Exception): From 46c9ea940324dcd56cd59b7d6257bfbcd11930b7 Mon Sep 17 00:00:00 2001 From: Peter Surda Date: Mon, 20 Mar 2017 18:32:26 +0100 Subject: [PATCH 026/407] Async network updates (WIP) - cleaner command handling - separating into header and command handling - incoming connection handler - bugfixes and more debug information --- src/bmproto.py | 150 +++++++++++++++++------------ src/network/advanceddispatcher.py | 5 +- src/network/asyncore_pollchoose.py | 2 + src/protocol.py | 2 +- 4 files changed, 93 insertions(+), 66 deletions(-) diff --git a/src/bmproto.py b/src/bmproto.py index 8bd90aff..0a147d3e 100644 --- a/src/bmproto.py +++ b/src/bmproto.py @@ -15,91 +15,94 @@ class BMProtoError(ProxyError): pass class BMConnection(AdvancedDispatcher): - def __init__(self, address): - AdvancedDispatcher.__init__(self) - self.destination = address + # ~1.6 MB which is the maximum possible size of an inv message. + maxMessageSize = 1600100 + + def __init__(self, address=None, sock=None): + AdvancedDispatcher.__init__(self, sock) + self.verackReceived = False + self.verackSent = False + if address is None and sock is not None: + self.destination = self.addr() + self.isOutbound = False + print "received connection in background from %s:%i" % (self.destination[0], self.destination[1]) + else: + self.destination = address + self.isOutbound = True + self.create_socket(socket.AF_INET, socket.SOCK_STREAM) + self.connect(self.destination) + print "connecting in background to %s:%i" % (self.destination[0], self.destination[1]) + + def bm_proto_reset(self): + self.magic = None + self.command = None + self.payloadLength = None + self.checksum = None self.payload = None - self.create_socket(socket.AF_INET, socket.SOCK_STREAM) - self.connect(self.destination) - print "connecting in background to %s:%i" % (self.destination[0], self.destination[1]) - - def bm_proto_len_sufficient(self): - if len(self.read_buf) < protocol.Header.size: - print "Length below header size" - return False - if not self.payload: - self.magic, self.command, self.payloadLength, self.checksum = protocol.Header.unpack(self.read_buf[:protocol.Header.size]) - self.command = self.command.rstrip('\x00') - if self.payloadLength > 1600100: # ~1.6 MB which is the maximum possible size of an inv message. - return False - if len(self.read_buf) < self.payloadLength + protocol.Header.size: - print "Length below announced object length" - return False - self.payload = self.read_buf[protocol.Header.size:self.payloadLength + protocol.Header.size] - return True - - def bm_check_command(self): - if self.magic != 0xE9BEB4D9: - self.set_state("crap", protocol.Header.size) - print "Bad magic" - self.payload = None - return False - if self.checksum != hashlib.sha512(self.payload).digest()[0:4]: - self.set_state("crap", protocol.Header.size) - self.payload = None - print "Bad checksum" - return False - print "received %s (%ib)" % (self.command, self.payloadLength) - return True + self.invalid = False def state_init(self): + self.bm_proto_reset() self.write_buf += protocol.assembleVersionMessage(self.destination[0], self.destination[1], (1,), False) if True: print "Sending version (%ib)" % len(self.write_buf) - self.set_state("bm_reccommand", 0) + self.set_state("bm_header", 0) return False - def state_bm_reccommand(self): - if not self.bm_proto_len_sufficient(): + def state_bm_header(self): + if len(self.read_buf) < protocol.Header.size: + print "Length below header size" return False - if not self.bm_check_command(): + self.magic, self.command, self.payloadLength, self.checksum = protocol.Header.unpack(self.read_buf[:protocol.Header.size]) + self.command = self.command.rstrip('\x00') + if self.magic != 0xE9BEB4D9: + # skip 1 byte in order to sync + self.bm_proto_reset() + self.set_state("bm_header", 1) + print "Bad magic" + if self.payloadLength > BMConnection.maxMessageSize: + self.invalid = True + self.set_state("bm_command", protocol.Header.size) + return True + + def state_bm_command(self): + if len(self.read_buf) < self.payloadLength: + print "Length below announced object length" return False - if self.command == "version": - self.bm_recversion() - elif self.command == "verack": - self.bm_recverack() + print "received %s (%ib)" % (self.command, self.payloadLength) + self.payload = self.read_buf[:self.payloadLength] + if self.checksum != hashlib.sha512(self.payload).digest()[0:4]: + print "Bad checksum, ignoring" + self.invalid = True + if not self.invalid: + try: + getattr(self, "bm_command_" + str(self.command))() + except AttributeError: + # unimplemented command + print "unimplemented command %s" % (self.command) else: - print "unimplemented command %s" % (self.command) - self.set_state("bm_reccommand", protocol.Header.size + self.payloadLength) - self.payload = None - print "buffer length = %i" % (len(self.read_buf)) - return False + print "Skipping command %s due to invalid data" % (self.command) + self.set_state("bm_header", self.payloadLength) + self.bm_proto_reset() + return True - def bm_recverack(self): + def bm_command_verack(self): self.verackReceived = True - return False + return True - def bm_recversion(self): - self.remoteProtocolVersion, self.services, timestamp, padding1, self.myExternalIP, padding2, self.remoteNodeIncomingPort = protocol.VersionPacket.unpack(self.payload[:82]) + def bm_command_version(self): + self.remoteProtocolVersion, self.services, self.timestamp, padding1, self.myExternalIP, padding2, self.remoteNodeIncomingPort = protocol.VersionPacket.unpack(self.payload[:protocol.VersionPacket.size]) print "remoteProtocolVersion: %i" % (self.remoteProtocolVersion) print "services: %08X" % (self.services) - print "time offset: %i" % (timestamp - int(time.time())) + print "time offset: %i" % (self.timestamp - int(time.time())) print "my external IP: %s" % (socket.inet_ntoa(self.myExternalIP)) print "remote node incoming port: %i" % (self.remoteNodeIncomingPort) - useragentLength, lengthofUseragentVarint = addresses.decodeVarint(self.payload[80:84]) + useragentLength, lengthOfUseragentVarint = addresses.decodeVarint(self.payload[80:84]) readPosition = 80 + lengthOfUseragentVarint self.userAgent = self.payload[readPosition:readPosition + useragentLength] readPosition += useragentLength print "user agent: %s" % (self.userAgent) - return False - - def state_http_request_sent(self): - if len(self.read_buf) > 0: - print self.read_buf - self.read_buf = b"" - if not self.connected: - self.set_state("close", 0) - return False + return True class Socks5BMConnection(Socks5Connection, BMConnection): @@ -120,6 +123,27 @@ class Socks4aBMConnection(Socks4aConnection, BMConnection): return False +class BMServer(AdvancedDispatcher): + port = 8444 + + def __init__(self, port=None): + if not hasattr(self, '_map'): + AdvancedDispatcher.__init__(self) + self.create_socket(socket.AF_INET, socket.SOCK_STREAM) + self.set_reuse_addr() + if port is None: + port = BMServer.port + self.bind(('127.0.0.1', port)) + self.connections = 0 + self.listen(5) + + def handle_accept(self): + pair = self.accept() + if pair is not None: + sock, addr = pair + BMConnection(sock=sock) + + if __name__ == "__main__": # initial fill diff --git a/src/network/advanceddispatcher.py b/src/network/advanceddispatcher.py index c672f9f9..df6e58ef 100644 --- a/src/network/advanceddispatcher.py +++ b/src/network/advanceddispatcher.py @@ -3,9 +3,9 @@ import asyncore_pollchoose as asyncore class AdvancedDispatcher(asyncore.dispatcher): _buf_len = 2097152 # 2MB - def __init__(self): + def __init__(self, sock=None): if not hasattr(self, '_map'): - asyncore.dispatcher.__init__(self) + asyncore.dispatcher.__init__(self, sock) self.read_buf = b"" self.write_buf = b"" self.state = "init" @@ -27,6 +27,7 @@ class AdvancedDispatcher(asyncore.dispatcher): return while True: try: + print "Trying to handle state \"%s\"" % (self.state) if getattr(self, "state_" + str(self.state))() is False: break except AttributeError: diff --git a/src/network/asyncore_pollchoose.py b/src/network/asyncore_pollchoose.py index 9f231e0e..7fa19f4a 100644 --- a/src/network/asyncore_pollchoose.py +++ b/src/network/asyncore_pollchoose.py @@ -287,6 +287,8 @@ def loop(timeout=30.0, use_poll=False, map=None, count=None, elif hasattr(select, 'select'): poller = select_poller + print "Poll loop using %s" % (poller.__name__) + if count is None: while map: poller(timeout, map) diff --git a/src/protocol.py b/src/protocol.py index a889ab93..9698f917 100644 --- a/src/protocol.py +++ b/src/protocol.py @@ -34,7 +34,7 @@ eightBytesOfRandomDataUsedToDetectConnectionsToSelf = pack( #New code should use CreatePacket instead of Header.pack Header = Struct('!L12sL4s') -VersionPacket = Struct('>LqQ20sI36sH') +VersionPacket = Struct('>LqQ20s4s36sH') # Bitfield From e97df3ad4de58adffada3804866f8ede9ab8ef10 Mon Sep 17 00:00:00 2001 From: Justin Ramos Date: Wed, 22 Mar 2017 20:47:48 +0000 Subject: [PATCH 027/407] print SystemExit exception messages instead of using the current build failure text Signed-off-by: Justin Ramos --- setup.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/setup.py b/setup.py index 5ea5a624..404c10fb 100644 --- a/setup.py +++ b/setup.py @@ -256,7 +256,9 @@ if __name__ == "__main__": }, scripts=['src/pybitmessage'] ) - except SystemExit: + except SystemExit as err: + print err.message + except: print "It looks like building the package failed.\n" \ "You may be missing a C++ compiler and the OpenSSL headers." compilerToPackages() From 9aacef144b4147fb1590dcbae80b663184ecb875 Mon Sep 17 00:00:00 2001 From: Dmitri Bogomolov <4glitch@gmail.com> Date: Sat, 25 Mar 2017 11:48:45 +0200 Subject: [PATCH 028/407] Moved OS and package detection care to setuptools install command --- setup.py | 62 +++++++++++++++++++++++++++++++------------------------- 1 file changed, 34 insertions(+), 28 deletions(-) diff --git a/setup.py b/setup.py index 404c10fb..67b2a525 100644 --- a/setup.py +++ b/setup.py @@ -4,8 +4,10 @@ import os import sys try: from setuptools import setup, Extension + from setuptools.command.install import install haveSetuptools = True except ImportError: + install = object haveSetuptools = False from importlib import import_module @@ -127,7 +129,7 @@ def detectOS(): return detectOS.result -def detectPrereqs(missing=False): +def detectPrereqs(missing=True): available = [] for module in packageName.keys(): try: @@ -144,47 +146,51 @@ def prereqToPackages(): print "You can install the requirements by running, as root:" print "%s %s" % ( packageManager[detectOS()], " ".join( - packageName[x][detectOS()] for x in detectPrereqs(True))) - for package in detectPrereqs(True): - try: - if packageName[package]['optional']: - print packageName[package]['description'] - except KeyError: - pass + packageName[x][detectOS()] for x in detectPrereqs())) + for package in detectPrereqs(): + if packageName[package].get('optional'): + print packageName[package].get('description') + def compilerToPackages(): if not detectOS() in compiling: return print "You can install the requirements by running, as root:" print "%s %s" % ( - packageManager[detectOS()], compiling[detectOS()]) + packageManager[detectOS.result], compiling[detectOS.result]) -if __name__ == "__main__": - detectOS.result = None - detectPrereqs.result = None - if detectPrereqs(True) != [] and detectOS() in packageManager: - if detectOS() is not None: + +class InstallCmd(install): + def run(self): + detectOS.result = None + prereqs = detectPrereqs() + if prereqs and detectOS() in packageManager: print "It looks like you're using %s. " \ "It is highly recommended to use the package manager " \ - "instead of setuptools." % (detectOS()) + "instead of setuptools." % (detectOS.result) prereqToPackages() try: - for module in detectPrereqs(True): + for module in prereqs: if not packageName[module]['optional']: sys.exit() except KeyError: sys.exit() - if not haveSetuptools: - print "It looks like you're missing setuptools." - sys.exit() - if detectPrereqs(True) != [] and sys.stdin.isatty(): - print "Press Return to continue" - try: - nothing = raw_input() - except NameError: - pass + if not haveSetuptools: + print "It looks like you're missing setuptools." + sys.exit() + if prereqs and sys.stdin.isatty(): + print "Press Return to continue" + try: + raw_input() + except NameError: + pass + + return install.run(self) + + +if __name__ == "__main__": here = os.path.abspath(os.path.dirname(__file__)) with open(os.path.join(here, 'README.md')) as f: README = f.read() @@ -197,7 +203,7 @@ if __name__ == "__main__": installRequires = [] # this will silently accept alternative providers of msgpack if they are already installed - if "msgpack" in detectPrereqs(True): + if "msgpack" in detectPrereqs(): installRequires.append("msgpack-python") try: @@ -254,7 +260,8 @@ if __name__ == "__main__": # 'pybitmessage = pybitmessage.bitmessagemain:main' # ] }, - scripts=['src/pybitmessage'] + scripts=['src/pybitmessage'], + cmdclass={'install': InstallCmd} ) except SystemExit as err: print err.message @@ -262,4 +269,3 @@ if __name__ == "__main__": print "It looks like building the package failed.\n" \ "You may be missing a C++ compiler and the OpenSSL headers." compilerToPackages() - From c1bdcc2aba3df3c554b624afee52b9263d0c14ed Mon Sep 17 00:00:00 2001 From: Peter Surda Date: Tue, 4 Apr 2017 10:43:29 +0200 Subject: [PATCH 029/407] ACKdata handling changes - any type of object can now serve as ACKdata --- src/class_objectProcessor.py | 31 ++++++++++++++++++------------- 1 file changed, 18 insertions(+), 13 deletions(-) diff --git a/src/class_objectProcessor.py b/src/class_objectProcessor.py index 446e09ab..e13c262c 100644 --- a/src/class_objectProcessor.py +++ b/src/class_objectProcessor.py @@ -56,6 +56,8 @@ class objectProcessor(threading.Thread): while True: objectType, data = queues.objectProcessorQueue.get() + self.checkackdata(data) + try: if objectType == 0: # getpubkey self.processgetpubkey(data) @@ -86,6 +88,22 @@ class objectProcessor(threading.Thread): logger.debug('Saved %s objects from the objectProcessorQueue to disk. objectProcessorThread exiting.' % str(numberOfObjectsThatWereInTheObjectProcessorQueue)) state.shutdown = 2 break + + def checkackdata(self, data): + # Let's check whether this is a message acknowledgement bound for us. + if len(data) < 32: + return + if data[-32:] in shared.ackdataForWhichImWatching: + logger.info('This object is an acknowledgement bound for me.') + del shared.ackdataForWhichImWatching[data[-32:]] + sqlExecute('UPDATE sent SET status=?, lastactiontime=? WHERE ackdata=?', + 'ackreceived', + int(time.time()), + data[-32:]) + queues.UISignalQueue.put(('updateSentItemStatusByAckdata', (data[-32:], tr._translate("MainWindow",'Acknowledgement of the message received %1').arg(l10n.formatTimestamp())))) + else: + logger.debug('This object is not an acknowledgement bound for me.') + def processgetpubkey(self, data): readPosition = 20 # bypass the nonce, time, and object type @@ -322,19 +340,6 @@ class objectProcessor(threading.Thread): readPosition += streamNumberAsClaimedByMsgLength inventoryHash = calculateInventoryHash(data) initialDecryptionSuccessful = False - # Let's check whether this is a message acknowledgement bound for us. - if data[-32:] in shared.ackdataForWhichImWatching: - logger.info('This msg IS an acknowledgement bound for me.') - del shared.ackdataForWhichImWatching[data[-32:]] - sqlExecute('UPDATE sent SET status=?, lastactiontime=? WHERE ackdata=?', - 'ackreceived', - int(time.time()), - data[-32:]) - queues.UISignalQueue.put(('updateSentItemStatusByAckdata', (data[-32:], tr._translate("MainWindow",'Acknowledgement of the message received %1').arg(l10n.formatTimestamp())))) - return - else: - logger.info('This was NOT an acknowledgement bound for me.') - # This is not an acknowledgement bound for me. See if it is a message # bound for me by trying to decrypt it with my private keys. From fe93473fc5a7149015e1e68027a1e6ccd5507eae Mon Sep 17 00:00:00 2001 From: Peter Surda Date: Tue, 4 Apr 2017 10:44:53 +0200 Subject: [PATCH 030/407] getpubkey length handling - don't try to process getpubkey that is too long --- src/class_objectProcessor.py | 3 +++ src/shared.py | 2 -- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/src/class_objectProcessor.py b/src/class_objectProcessor.py index e13c262c..ad78bd87 100644 --- a/src/class_objectProcessor.py +++ b/src/class_objectProcessor.py @@ -106,6 +106,9 @@ class objectProcessor(threading.Thread): def processgetpubkey(self, data): + if len(data) > 200: + logger.info('getpubkey is abnormally long. Sanity check failed. Ignoring object.') + return readPosition = 20 # bypass the nonce, time, and object type requestedAddressVersionNumber, addressVersionLength = decodeVarint( data[readPosition:readPosition + 10]) diff --git a/src/shared.py b/src/shared.py index 69794033..4fd66610 100644 --- a/src/shared.py +++ b/src/shared.py @@ -432,8 +432,6 @@ def _checkAndShareGetpubkeyWithPeers(data): if len(data) < 42: logger.info('getpubkey message doesn\'t contain enough data. Ignoring.') return - if len(data) > 200: - logger.info('getpubkey is abnormally long. Sanity check failed. Ignoring object.') embeddedTime, = unpack('>Q', data[8:16]) readPosition = 20 # bypass the nonce, time, and object type requestedAddressVersionNumber, addressVersionLength = decodeVarint( From 51aeb284ca8b5a6f78d8eb6528c4e733bfc656fc Mon Sep 17 00:00:00 2001 From: Peter Surda Date: Tue, 4 Apr 2017 10:46:01 +0200 Subject: [PATCH 031/407] Async network IO updates - WIP --- src/bmproto.py | 192 ++++++++++++++++++++++++++++++++++++++++++++- src/network/tls.py | 70 ++++++++--------- 2 files changed, 222 insertions(+), 40 deletions(-) diff --git a/src/bmproto.py b/src/bmproto.py index 0a147d3e..5cd08779 100644 --- a/src/bmproto.py +++ b/src/bmproto.py @@ -9,14 +9,19 @@ from network.socks5 import Socks5Connection, Socks5Resolver, Socks5AuthError, So from network.socks4a import Socks4aConnection, Socks4aResolver, Socks4aError import addresses +from bmconfigparser import BMConfigParser import protocol class BMProtoError(ProxyError): pass -class BMConnection(AdvancedDispatcher): +class BMConnection(TLSDispatcher): # ~1.6 MB which is the maximum possible size of an inv message. maxMessageSize = 1600100 + # protocol specification says max 1000 addresses in one addr command + maxAddrCount = 1000 + # protocol specification says max 50000 objects in one inv command + maxObjectCount = 50000 def __init__(self, address=None, sock=None): AdvancedDispatcher.__init__(self, sock) @@ -25,12 +30,14 @@ class BMConnection(AdvancedDispatcher): if address is None and sock is not None: self.destination = self.addr() self.isOutbound = False + TLSHandshake.__init__(self, sock, server_side=True) print "received connection in background from %s:%i" % (self.destination[0], self.destination[1]) else: self.destination = address self.isOutbound = True self.create_socket(socket.AF_INET, socket.SOCK_STREAM) self.connect(self.destination) + TLSHandshake.__init__(self, sock, server_side=False) print "connecting in background to %s:%i" % (self.destination[0], self.destination[1]) def bm_proto_reset(self): @@ -49,6 +56,11 @@ class BMConnection(AdvancedDispatcher): self.set_state("bm_header", 0) return False + def state_bm_ready(self): + self.sendAddr() + self.sendBigInv() + return True + def state_bm_header(self): if len(self.read_buf) < protocol.Header.size: print "Length below header size" @@ -74,18 +86,27 @@ class BMConnection(AdvancedDispatcher): if self.checksum != hashlib.sha512(self.payload).digest()[0:4]: print "Bad checksum, ignoring" self.invalid = True + retval = True if not self.invalid: try: - getattr(self, "bm_command_" + str(self.command))() + retval = getattr(self, "bm_command_" + str(self.command).lower())() except AttributeError: # unimplemented command print "unimplemented command %s" % (self.command) else: print "Skipping command %s due to invalid data" % (self.command) - self.set_state("bm_header", self.payloadLength) - self.bm_proto_reset() + if retval: + self.set_state("bm_header", self.payloadLength) + self.bm_proto_reset() + # else assume the command requires a different state to follow return True + def bm_command_error(self): + def bm_command_getdata(self): + def bm_command_object(self): + def bm_command_ping(self): + def bm_command_pong(self): + def bm_command_verack(self): self.verackReceived = True return True @@ -102,8 +123,171 @@ class BMConnection(AdvancedDispatcher): self.userAgent = self.payload[readPosition:readPosition + useragentLength] readPosition += useragentLength print "user agent: %s" % (self.userAgent) + if not self.peerValidityChecks(): + # TODO ABORT + return True + self.write_buf += protocol.CreatePacket('verack') + self.verackSent = True + if ((self.services & protocol.NODE_SSL == protocol.NODE_SSL) and + protocol.haveSSL(not self.isOutbound)): + self.isSSL = True + if self.verackReceived: + if self.isSSL: + self.set_state("tls_init", self.payloadLength) + else: + self.set_state("bm_ready", self.payloadLength) + self.bm_proto_reset() + return False + + def peerValidityChecks(self): + if self.remoteProtocolVersion < 3: + self.write_buf += protocol.assembleErrorMessage(fatal=2, + errorText="Your is using an old protocol. Closing connection.") + logger.debug ('Closing connection to old protocol version %s, node: %s', + str(self.remoteProtocolVersion), str(self.peer)) + return False + if self.timeOffset > 3600: + self.write_buf += protocol.assembleErrorMessage(fatal=2, + errorText="Your time is too far in the future compared to mine. Closing connection.") + logger.info("%s's time is too far in the future (%s seconds). Closing connection to it.", + self.peer, self.timeOffset) + shared.timeOffsetWrongCount += 1 + return False + elif self.timeOffset < -3600: + self.write_buf += protocol.assembleErrorMessage(fatal=2, + errorText="Your time is too far in the past compared to mine. Closing connection.") + logger.info("%s's time is too far in the past (timeOffset %s seconds). Closing connection to it.", + self.peer, self.timeOffset) + shared.timeOffsetWrongCount += 1 + return False + else: + shared.timeOffsetWrongCount = 0 + if len(self.streams) == 0: + self.write_buf += protocol.assembleErrorMessage(fatal=2, + errorText="We don't have shared stream interests. Closing connection."))) + logger.debug ('Closed connection to %s because there is no overlapping interest in streams.', + str(self.peer)) + return False return True + def sendAddr(self): + def sendChunk(): + if numberOfAddressesInAddrMessage == 0: + return + self.write_buf += protocol.CreatePacket('addr', \ + addresses.encodeVarint(numberOfAddressesInAddrMessage) + payload))) + + # We are going to share a maximum number of 1000 addrs (per overlapping + # stream) with our peer. 500 from overlapping streams, 250 from the + # left child stream, and 250 from the right child stream. + maxAddrCount = BMConfigParser().safeGetInt("bitmessagesettings", "maxaddrperstreamsend", 500) + + # init + addressCount = 0 + payload = '' + + for stream in self.streams: + addrsInMyStream = {} + addrsInChildStreamLeft = {} + addrsInChildStreamRight = {} + + with knownnodes.knownNodesLock: + if len(knownnodes.knownNodes[stream]) > 0: + filtered = {k: v for k, v in knownnodes.knownNodes[stream].items() + if v > (int(time.time()) - shared.maximumAgeOfNodesThatIAdvertiseToOthers)} + elemCount = len(filtered) + if elemCount > maxAddrCount: + elemCount = maxAddrCount + # only if more recent than 3 hours + addrsInMyStream = random.sample(filtered.items(), elemCount) + # sent 250 only if the remote isn't interested in it + if len(knownnodes.knownNodes[stream * 2]) > 0 and stream not in self.streams: + filtered = {k: v for k, v in knownnodes.knownNodes[stream*2].items() + if v > (int(time.time()) - shared.maximumAgeOfNodesThatIAdvertiseToOthers)} + elemCount = len(filtered) + if elemCount > maxAddrCount / 2: + elemCount = int(maxAddrCount / 2) + addrsInChildStreamLeft = random.sample(filtered.items(), elemCount) + if len(knownnodes.knownNodes[(stream * 2) + 1]) > 0 and stream not in self.streams: + filtered = {k: v for k, v in knownnodes.knownNodes[stream*2+1].items() + if v > (int(time.time()) - shared.maximumAgeOfNodesThatIAdvertiseToOthers)} + elemCount = len(filtered) + if elemCount > maxAddrCount / 2: + elemCount = int(maxAddrCount / 2) + addrsInChildStreamRight = random.sample(filtered.items(), elemCount) + for (HOST, PORT), timeLastReceivedMessageFromThisNode in addrsInMyStream: + addressCount += 1 + payload += pack( + '>Q', timeLastReceivedMessageFromThisNode) # 64-bit time + payload += pack('>I', stream) + payload += pack( + '>q', 1) # service bit flags offered by this node + payload += protocol.encodeHost(HOST) + payload += pack('>H', PORT) # remote port + if addressCount >= BMConnection.maxAddrCount: + sendChunk() + payload = '' + addressCount = 0 + for (HOST, PORT), timeLastReceivedMessageFromThisNode in addrsInChildStreamLeft: + addressCount += 1 + payload += pack( + '>Q', timeLastReceivedMessageFromThisNode) # 64-bit time + payload += pack('>I', stream * 2) + payload += pack( + '>q', 1) # service bit flags offered by this node + payload += protocol.encodeHost(HOST) + payload += pack('>H', PORT) # remote port + if addressCount >= BMConnection.maxAddrCount: + sendChunk() + payload = '' + addressCount = 0 + for (HOST, PORT), timeLastReceivedMessageFromThisNode in addrsInChildStreamRight: + addressCount += 1 + payload += pack( + '>Q', timeLastReceivedMessageFromThisNode) # 64-bit time + payload += pack('>I', (stream * 2) + 1) + payload += pack( + '>q', 1) # service bit flags offered by this node + payload += protocol.encodeHost(HOST) + payload += pack('>H', PORT) # remote port + if addressCount >= BMConnection.maxAddrCount: + sendChunk() + payload = '' + addressCount = 0 + + # flush + sendChunk() + + def sendBigInv(self): + def sendChunk(): + if objectCount == 0: + return + payload = encodeVarint(objectCount) + payload + logger.debug('Sending huge inv message with %i objects to just this one peer', + str(numberOfObjects)) + self.write_buf += protocol.CreatePacket('inv', payload) + + # Select all hashes for objects in this stream. + bigInvList = {} + for stream in self.streams: + for hash in Inventory().unexpired_hashes_by_stream(stream): + if not self.objectHashHolderInstance.hasHash(hash): + bigInvList[hash] = 0 + objectCount = 0 + payload = '' + # Now let us start appending all of these hashes together. They will be + # sent out in a big inv message to our new peer. + for hash, storedValue in bigInvList.items(): + payload += hash + objectCount += 1 + if objectCount >= BMConnection.maxObjectCount: + self.sendChunk() + payload = '' + objectCount = 0 + + # flush + sendChunk() + class Socks5BMConnection(Socks5Connection, BMConnection): def __init__(self, address): diff --git a/src/network/tls.py b/src/network/tls.py index 8f104c55..023f6cac 100644 --- a/src/network/tls.py +++ b/src/network/tls.py @@ -2,57 +2,45 @@ SSL/TLS negotiation. """ -import asyncore +from network.advanceddispatcher import AdvancedDispatcher +import network.asyncore_pollchoose as asyncore import socket import ssl import sys import protocol -class TLSHandshake(asyncore.dispatcher): - """ - Negotiates a SSL/TLS connection before handing itself spawning a - dispatcher that can deal with the overlying protocol as soon as the - handshake has been completed. - - `handoff` is a function/method called when the handshake has completed. - `address` is a tuple consisting of hostname/address and port to connect to - if nothing is passed in `sock`, which can take an already-connected socket. - `certfile` can take a path to a certificate bundle, and `server_side` - indicates whether the socket is intended to be a server-side or client-side - socket. - """ - +class TLSDispatcher(AdvancedDispatcher): def __init__(self, address=None, sock=None, - certfile=None, keyfile=None, server_side=False, ciphers=None, init_parent=True): - if not hasattr(self, '_map'): - asyncore.dispatcher.__init__(self, sock) + certfile=None, keyfile=None, server_side=False, ciphers=protocol.sslProtocolCiphers): self.want_read = self.want_write = True - self.certfile = certfile - self.keyfile = keyfile + if certfile is None: + self.certfile = os.path.join(paths.codePath(), 'sslkeys', 'cert.pem') + else: + self.certfile = certfile + if keyfile is None: + self.keyfile = os.path.join(paths.codePath(), 'sslkeys', 'key.pem') + else: + self.keyfile = keyfile self.server_side = server_side self.ciphers = ciphers + self.tlsStarted = False self.tlsDone = False - if sock is None: - self.create_socket(socket.AF_INET, socket.SOCK_STREAM) -# logger.info('Connecting to %s%d', address[0], address[1]) - self.connect(address) - elif self.connected: - # Initiate the handshake for an already-connected socket. - self.handle_connect() + self.isSSL = False - def handle_connect(self): + def state_tls_init(self): + self.isSSL = True # Once the connection has been established, it's safe to wrap the # socket. if sys.version_info >= (2,7,9): context = ssl.create_default_context(purpose = ssl.Purpose.SERVER_AUTH if self.server_side else ssl.Purpose.CLIENT_AUTH) context.set_ciphers(self.ciphers) - # context.set_ecdh_curve("secp256k1") + context.set_ecdh_curve("secp256k1") context.check_hostname = False context.verify_mode = ssl.CERT_NONE # also exclude TLSv1 and TLSv1.1 in the future - context.options |= ssl.OP_NOSSLv2 | ssl.OP_NOSSLv3 - self.sslSock = context.wrap_socket(self.sock, server_side = self.server_side, do_handshake_on_connect=False) + context.options = ssl.OP_ALL | ssl.OP_NO_SSLv2 | ssl.OP_NO_SSLv3 | ssl.OP_SINGLE_ECDH_USE | ssl.OP_CIPHER_SERVER_PREFERENCE + self.sslSocket = context.wrap_socket(self.sock, server_side = self.server_side, do_handshake_on_connect=False) else: self.sslSocket = ssl.wrap_socket(self.socket, server_side=self.server_side, @@ -67,20 +55,30 @@ class TLSHandshake(asyncore.dispatcher): # self.socket.context.set_ecdh_curve("secp256k1") def writable(self): - return self.want_write + if self.tlsStarted and not self.tlsDone: + return self.want_write + else: + return AdvancedDispacher.writable(self) def readable(self): - return self.want_read + if self.tlsStarted and not self.tlsDone: + return self.want_read + else: + return AdvancedDispacher.readable(self) def handle_read(self): - if not self.tlsDone: + if self.tlsStarted and not self.tlsDone: self._handshake() + else: + return AdvancedDispacher.handle_read(self) def handle_write(self): - if not self.tlsDone: + if self.tlsStarted and not not self.tlsDone: self._handshake() + else: + return AdvancedDispacher.handle_write(self) - def _handshake(self): + def state_tls_handshake(self): """ Perform the handshake. """ From e832ea168957ec04821aac9a886bd9e3a4779696 Mon Sep 17 00:00:00 2001 From: Peter Surda Date: Tue, 4 Apr 2017 12:31:52 +0200 Subject: [PATCH 032/407] setup.py fix nested list --- setup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.py b/setup.py index 67b2a525..fc9e367a 100644 --- a/setup.py +++ b/setup.py @@ -220,7 +220,7 @@ if __name__ == "__main__": url='https://bitmessage.org', # TODO: add keywords #keywords='', - install_requires=[installRequires], + install_requires=installRequires, extras_require={ 'qrcode': ['qrcode'], 'pyopencl': ['pyopencl'] From e6f0b34f9b1574019623cb3b6ce8e4a0803a2a13 Mon Sep 17 00:00:00 2001 From: Dmitri Bogomolov <4glitch@gmail.com> Date: Tue, 28 Mar 2017 17:38:05 +0300 Subject: [PATCH 033/407] Fixed some inconvenience on first run mainly in Ubuntu. - immediately return from initCL() if numpy or pyopencl is unevailable (no ImportError because of resetPoW() call) - use glob to find C extension even if it named like `bitmsghash.x86_64-linux-gnu.so` If user chooses to show the Settings dialog: - activate the "Network Settings" tab - remove option 'dontconnect' if settings have been saved --- src/bitmessageqt/__init__.py | 16 ++++++++++++---- src/openclpow.py | 7 ++++--- src/proofofwork.py | 11 ++++++++++- 3 files changed, 26 insertions(+), 8 deletions(-) diff --git a/src/bitmessageqt/__init__.py b/src/bitmessageqt/__init__.py index e861f767..76f6e46d 100644 --- a/src/bitmessageqt/__init__.py +++ b/src/bitmessageqt/__init__.py @@ -1619,7 +1619,8 @@ class MyForm(settingsmixin.SMainWindow): self.connectDialogInstance = connectDialog(self) if self.connectDialogInstance.exec_(): if self.connectDialogInstance.ui.radioButtonConnectNow.isChecked(): - BMConfigParser().remove_option('bitmessagesettings', 'dontconnect') + BMConfigParser().remove_option( + 'bitmessagesettings', 'dontconnect') BMConfigParser().save() else: self.click_actionSettings() @@ -2349,7 +2350,12 @@ class MyForm(settingsmixin.SMainWindow): def click_actionSettings(self): self.settingsDialogInstance = settingsDialog(self) + if self._firstrun: + self.settingsDialogInstance.ui.tabWidgetSettings.setCurrentIndex(1) if self.settingsDialogInstance.exec_(): + if self._firstrun: + BMConfigParser().remove_option( + 'bitmessagesettings', 'dontconnect') BMConfigParser().set('bitmessagesettings', 'startonlogon', str( self.settingsDialogInstance.ui.checkBoxStartOnLogon.isChecked())) BMConfigParser().set('bitmessagesettings', 'minimizetotray', str( @@ -4517,9 +4523,11 @@ def run(): myapp.appIndicatorInit(app) myapp.ubuntuMessagingMenuInit() myapp.notifierInit() - if BMConfigParser().safeGetBoolean('bitmessagesettings', 'dontconnect'): - myapp.showConnectDialog() # ask the user if we may connect - + myapp._firstrun = BMConfigParser().safeGetBoolean( + 'bitmessagesettings', 'dontconnect') + if myapp._firstrun: + myapp.showConnectDialog() # ask the user if we may connect + # try: # if BMConfigParser().get('bitmessagesettings', 'mailchuck') < 1: # myapp.showMigrationWizard(BMConfigParser().get('bitmessagesettings', 'mailchuck')) diff --git a/src/openclpow.py b/src/openclpow.py index 59375329..ca40e634 100644 --- a/src/openclpow.py +++ b/src/openclpow.py @@ -26,7 +26,9 @@ except: libAvailable = False def initCL(): - global ctx, queue, program, hash_dt + global ctx, queue, program, hash_dt, libAvailable + if libAvailable is False: + return del enabledGpus[:] del vendors[:] del gpus[:] @@ -98,8 +100,7 @@ def do_opencl_pow(hash, target): # logger.debug("Took %d tries.", progress) return output[0][0] -if libAvailable: - initCL() +initCL() if __name__ == "__main__": target = 54227212183L diff --git a/src/proofofwork.py b/src/proofofwork.py index 493b8b0c..eb845c25 100644 --- a/src/proofofwork.py +++ b/src/proofofwork.py @@ -266,9 +266,18 @@ def init(): else: try: bso = ctypes.CDLL(os.path.join(paths.codePath(), "bitmsghash", bitmsglib)) - logger.info("Loaded C PoW DLL %s", bitmsglib) + except OSError: + import glob + try: + bso = ctypes.CDLL(glob.glob(os.path.join( + paths.codePath(), "bitmsghash", "bitmsghash*.so" + ))[0]) + except (OSError, IndexError): + bso = None except: bso = None + else: + logger.info("Loaded C PoW DLL %s", bitmsglib) if bso: try: bmpow = bso.BitmessagePOW From 96d58f3c1191098dc1b7a22e960dfc691e32bbfb Mon Sep 17 00:00:00 2001 From: Peter Surda Date: Sun, 16 Apr 2017 18:27:15 +0200 Subject: [PATCH 034/407] Asyncore update (WIP) --- src/bmproto.py | 159 ++++++++++++++++++++++++----- src/network/advanceddispatcher.py | 16 ++- src/network/asyncore_pollchoose.py | 42 ++++++-- src/network/http.py | 2 +- src/network/node.py | 66 ++++++++++++ src/network/socks4a.py | 24 ++--- src/network/socks5.py | 24 ++--- src/network/tls.py | 52 +++++++--- src/protocol.py | 5 + 9 files changed, 306 insertions(+), 84 deletions(-) create mode 100644 src/network/node.py diff --git a/src/bmproto.py b/src/bmproto.py index 5cd08779..c9160c3b 100644 --- a/src/bmproto.py +++ b/src/bmproto.py @@ -1,15 +1,20 @@ import hashlib import time +from pprint import pprint import socket +from struct import unpack from network.advanceddispatcher import AdvancedDispatcher +from network.node import Node import network.asyncore_pollchoose as asyncore from network.proxy import Proxy, ProxyError, GeneralProxyError from network.socks5 import Socks5Connection, Socks5Resolver, Socks5AuthError, Socks5Error from network.socks4a import Socks4aConnection, Socks4aResolver, Socks4aError +from network.tls import TLSDispatcher import addresses from bmconfigparser import BMConfigParser +import shared import protocol class BMProtoError(ProxyError): pass @@ -30,14 +35,14 @@ class BMConnection(TLSDispatcher): if address is None and sock is not None: self.destination = self.addr() self.isOutbound = False - TLSHandshake.__init__(self, sock, server_side=True) + TLSDispatcher.__init__(self, sock, server_side=True) print "received connection in background from %s:%i" % (self.destination[0], self.destination[1]) else: self.destination = address self.isOutbound = True self.create_socket(socket.AF_INET, socket.SOCK_STREAM) self.connect(self.destination) - TLSHandshake.__init__(self, sock, server_side=False) + TLSDispatcher.__init__(self, sock, server_side=False) print "connecting in background to %s:%i" % (self.destination[0], self.destination[1]) def bm_proto_reset(self): @@ -47,19 +52,22 @@ class BMConnection(TLSDispatcher): self.checksum = None self.payload = None self.invalid = False + self.payloadOffset = 0 def state_init(self): self.bm_proto_reset() - self.write_buf += protocol.assembleVersionMessage(self.destination[0], self.destination[1], (1,), False) + self.append_write_buf(protocol.assembleVersionMessage(self.destination[0], self.destination[1], (1,), False)) if True: print "Sending version (%ib)" % len(self.write_buf) - self.set_state("bm_header", 0) + self.set_state("bm_header") return False def state_bm_ready(self): + print "doing bm ready" self.sendAddr() self.sendBigInv() - return True + self.set_state("bm_header") + return False def state_bm_header(self): if len(self.read_buf) < protocol.Header.size: @@ -101,32 +109,127 @@ class BMConnection(TLSDispatcher): # else assume the command requires a different state to follow return True + def decode_payload_string(self, length): + value = self.payload[self.payloadOffset:self.payloadOffset+length] + self.payloadOffset += length + return value + + def decode_payload_varint(self): + value, offset = addresses.decodeVarint(self.payload[self.payloadOffset:]) + self.payloadOffset += offset + return value + + def decode_payload_node(self): + services, address, port = self.decode_payload_content("Q16sH") + return Node(services, address, port) + + def decode_payload_content(self, pattern = "v"): + # l = varint indicating the length of the next item + # v = varint (or array) + # H = uint16 + # I = uint32 + # Q = uint64 + # i = net_addr (without time and stream number) + # s = string + # 0-9 = length of the next item + # , = end of array + + retval = [] + size = 0 + insideDigit = False + + for i in range(len(pattern)): + if pattern[i] in "0123456789": + size = size * 10 + int(pattern[i]) + continue + elif pattern[i] == "l": + size = self.decode_payload_varint() + continue + if size > 0: + innerval = [] + if pattern[i] == "s": + retval.append(self.payload[self.payloadOffset:self.payloadOffset + size]) + self.payloadOffset += size + else: + for j in range(size): + if "," in pattern[i:]: + retval.append(self.decode_payload_content(pattern[i:pattern.index(",")])) + else: + retval.append(self.decode_payload_content(pattern[i:])) + size = 0 + else: + if pattern[i] == "v": + retval.append(self.decode_payload_varint()) + if pattern[i] == "i": + retval.append(self.decode_payload_node()) + if pattern[i] == "H": + retval.append(unpack(">H", self.payload[self.payloadOffset:self.payloadOffset+2])[0]) + self.payloadOffset += 2 + if pattern[i] == "I": + retval.append(unpack(">I", self.payload[self.payloadOffset:self.payloadOffset+4])[0]) + self.payloadOffset += 4 + if pattern[i] == "Q": + retval.append(unpack(">Q", self.payload[self.payloadOffset:self.payloadOffset+8])[0]) + self.payloadOffset += 8 + return retval + def bm_command_error(self): + fatalStatus, banTime, inventoryVector, errorText = self.decode_payload_content("vvlsls") + def bm_command_getdata(self): + items = self.decode_payload_content("l32s") + #self.antiIntersectionDelay(True) # only handle getdata requests if we have been connected long enough + for i in items: + logger.debug('received getdata request for item:' + hexlify(i)) + if self.objectHashHolderInstance.hasHash(i): + self.antiIntersectionDelay() + else: + if i in Inventory(): + self.append_write_buf(protocol.CreatePacket('object', Inventory()[i].payload)) + else: + #self.antiIntersectionDelay() + logger.warning('%s asked for an object with a getdata which is not in either our memory inventory or our SQL inventory. We probably cleaned it out after advertising it but before they got around to asking for it.' % (self.peer,)) + def bm_command_object(self): + lengthOfTimeWeShouldUseToProcessThisMessage = shared.checkAndShareObjectWithPeers(self.payload) + self.downloadQueue.task_done(calculateInventoryHash(self.payload)) + + def bm_command_addr(self): + addresses = self.decode_payload_content("lQbQ16sH") + def bm_command_ping(self): + self.append_write_buf(protocol.CreatePacket('pong')) + def bm_command_pong(self): + # nothing really + pass def bm_command_verack(self): self.verackReceived = True - return True + if self.verackSent: + if self.isSSL: + self.set_state("tls_init", self.payloadLength) + else: + self.set_state("bm_ready", self.payloadLength) + else: + self.set_state("bm_header", self.payloadLength) + self.bm_proto_reset() + return False def bm_command_version(self): - self.remoteProtocolVersion, self.services, self.timestamp, padding1, self.myExternalIP, padding2, self.remoteNodeIncomingPort = protocol.VersionPacket.unpack(self.payload[:protocol.VersionPacket.size]) + #self.remoteProtocolVersion, self.services, self.timestamp, padding1, self.myExternalIP, padding2, self.remoteNodeIncomingPort = protocol.VersionPacket.unpack(self.payload[:protocol.VersionPacket.size]) + self.remoteProtocolVersion, self.services, self.timestamp, self.sockNode, self.peerNode, self.nonce, self.userAgent, self.streams = self.decode_payload_content("IQQiiQlslv") + self.timeOffset = self.timestamp - int(time.time()) print "remoteProtocolVersion: %i" % (self.remoteProtocolVersion) print "services: %08X" % (self.services) print "time offset: %i" % (self.timestamp - int(time.time())) - print "my external IP: %s" % (socket.inet_ntoa(self.myExternalIP)) - print "remote node incoming port: %i" % (self.remoteNodeIncomingPort) - useragentLength, lengthOfUseragentVarint = addresses.decodeVarint(self.payload[80:84]) - readPosition = 80 + lengthOfUseragentVarint - self.userAgent = self.payload[readPosition:readPosition + useragentLength] - readPosition += useragentLength + print "my external IP: %s" % (self.sockNode.address) + print "remote node incoming port: %i" % (self.peerNode.port) print "user agent: %s" % (self.userAgent) if not self.peerValidityChecks(): # TODO ABORT return True - self.write_buf += protocol.CreatePacket('verack') + self.append_write_buf(protocol.CreatePacket('verack')) self.verackSent = True if ((self.services & protocol.NODE_SSL == protocol.NODE_SSL) and protocol.haveSSL(not self.isOutbound)): @@ -141,21 +244,21 @@ class BMConnection(TLSDispatcher): def peerValidityChecks(self): if self.remoteProtocolVersion < 3: - self.write_buf += protocol.assembleErrorMessage(fatal=2, - errorText="Your is using an old protocol. Closing connection.") + self.append_write_buf(protocol.assembleErrorMessage(fatal=2, + errorText="Your is using an old protocol. Closing connection.")) logger.debug ('Closing connection to old protocol version %s, node: %s', str(self.remoteProtocolVersion), str(self.peer)) return False if self.timeOffset > 3600: - self.write_buf += protocol.assembleErrorMessage(fatal=2, - errorText="Your time is too far in the future compared to mine. Closing connection.") + self.append_write_buf(protocol.assembleErrorMessage(fatal=2, + errorText="Your time is too far in the future compared to mine. Closing connection.")) logger.info("%s's time is too far in the future (%s seconds). Closing connection to it.", self.peer, self.timeOffset) shared.timeOffsetWrongCount += 1 return False elif self.timeOffset < -3600: - self.write_buf += protocol.assembleErrorMessage(fatal=2, - errorText="Your time is too far in the past compared to mine. Closing connection.") + self.append_write_buf(protocol.assembleErrorMessage(fatal=2, + errorText="Your time is too far in the past compared to mine. Closing connection.")) logger.info("%s's time is too far in the past (timeOffset %s seconds). Closing connection to it.", self.peer, self.timeOffset) shared.timeOffsetWrongCount += 1 @@ -163,8 +266,8 @@ class BMConnection(TLSDispatcher): else: shared.timeOffsetWrongCount = 0 if len(self.streams) == 0: - self.write_buf += protocol.assembleErrorMessage(fatal=2, - errorText="We don't have shared stream interests. Closing connection."))) + self.append_write_buf(protocol.assembleErrorMessage(fatal=2, + errorText="We don't have shared stream interests. Closing connection.")) logger.debug ('Closed connection to %s because there is no overlapping interest in streams.', str(self.peer)) return False @@ -174,8 +277,8 @@ class BMConnection(TLSDispatcher): def sendChunk(): if numberOfAddressesInAddrMessage == 0: return - self.write_buf += protocol.CreatePacket('addr', \ - addresses.encodeVarint(numberOfAddressesInAddrMessage) + payload))) + self.append_write_buf(protocol.CreatePacket('addr', \ + addresses.encodeVarint(numberOfAddressesInAddrMessage) + payload)) # We are going to share a maximum number of 1000 addrs (per overlapping # stream) with our peer. 500 from overlapping streams, 250 from the @@ -265,7 +368,7 @@ class BMConnection(TLSDispatcher): payload = encodeVarint(objectCount) + payload logger.debug('Sending huge inv message with %i objects to just this one peer', str(numberOfObjects)) - self.write_buf += protocol.CreatePacket('inv', payload) + self.append_write_buf(protocol.CreatePacket('inv', payload)) # Select all hashes for objects in this stream. bigInvList = {} @@ -335,15 +438,15 @@ if __name__ == "__main__": direct = BMConnection(host) while len(asyncore.socket_map) > 0: print "loop, state = %s" % (direct.state) - asyncore.loop(timeout=1, count=1) + asyncore.loop(timeout=10, count=1) continue proxy = Socks5BMConnection(host) while len(asyncore.socket_map) > 0: # print "loop, state = %s" % (proxy.state) - asyncore.loop(timeout=1, count=1) + asyncore.loop(timeout=10, count=1) proxy = Socks4aBMConnection(host) while len(asyncore.socket_map) > 0: # print "loop, state = %s" % (proxy.state) - asyncore.loop(timeout=1, count=1) + asyncore.loop(timeout=10, count=1) diff --git a/src/network/advanceddispatcher.py b/src/network/advanceddispatcher.py index df6e58ef..dc7eedb0 100644 --- a/src/network/advanceddispatcher.py +++ b/src/network/advanceddispatcher.py @@ -10,11 +10,16 @@ class AdvancedDispatcher(asyncore.dispatcher): self.write_buf = b"" self.state = "init" - def slice_read_buf(self, length=0): - self.read_buf = self.read_buf[length:] + def append_write_buf(self, string = None): + self.write_buf += string def slice_write_buf(self, length=0): - self.write_buf = self.read_buf[length:] + if length > 0: + self.write_buf = self.write_buf[length:] + + def slice_read_buf(self, length=0): + if length > 0: + self.read_buf = self.read_buf[length:] def read_buf_sufficient(self, length=0): if len(self.read_buf) < length: @@ -23,7 +28,7 @@ class AdvancedDispatcher(asyncore.dispatcher): return True def process(self): - if self.state != "init" and len(self.read_buf) == 0: + if self.state not in ["init", "tls_handshake"] and len(self.read_buf) == 0: return while True: try: @@ -34,7 +39,7 @@ class AdvancedDispatcher(asyncore.dispatcher): # missing state raise - def set_state(self, state, length): + def set_state(self, state, length=0): self.slice_read_buf(length) self.state = state @@ -45,6 +50,7 @@ class AdvancedDispatcher(asyncore.dispatcher): return self.connecting or len(self.read_buf) < AdvancedDispatcher._buf_len def handle_read(self): + print "handle_read" self.read_buf += self.recv(AdvancedDispatcher._buf_len) self.process() diff --git a/src/network/asyncore_pollchoose.py b/src/network/asyncore_pollchoose.py index 7fa19f4a..4ccce7f9 100644 --- a/src/network/asyncore_pollchoose.py +++ b/src/network/asyncore_pollchoose.py @@ -60,6 +60,9 @@ from errno import EALREADY, EINPROGRESS, EWOULDBLOCK, ECONNRESET, EINVAL, \ _DISCONNECTED = frozenset((ECONNRESET, ENOTCONN, ESHUTDOWN, ECONNABORTED, EPIPE, EBADF)) +OP_READ = 1 +OP_WRITE = 2 + try: socket_map except NameError: @@ -178,17 +181,25 @@ def poll_poller(timeout=0.0, map=None): poll_poller.pollster = select.poll() if map: for fd, obj in list(map.items()): - flags = 0 + flags = newflags = 0 if obj.readable(): flags |= select.POLLIN | select.POLLPRI + newflags |= OP_READ + else: + newflags &= ~ OP_READ # accepting sockets should not be writable if obj.writable() and not obj.accepting: flags |= select.POLLOUT - if flags: - try: + newflags |= OP_WRITE + else: + newflags &= ~ OP_WRITE + if newflags != obj.flags: + obj.flags = newflags + if obj.poller_registered: poll_poller.pollster.modify(fd, flags) - except IOError: + else: poll_poller.pollster.register(fd, flags) + obj.poller_registered = True try: r = poll_poller.pollster.poll(timeout) except KeyboardInterrupt: @@ -213,19 +224,28 @@ def epoll_poller(timeout=0.0, map=None): epoll_poller.pollster = select.epoll() if map: for fd, obj in map.items(): - flags = 0 + flags = newflags = 0 if obj.readable(): flags |= select.POLLIN | select.POLLPRI - if obj.writable(): + newflags |= OP_READ + else: + newflags &= ~ OP_READ + # accepting sockets should not be writable + if obj.writable() and not obj.accepting: flags |= select.POLLOUT - if flags: + newflags |= OP_WRITE + else: + newflags &= ~ OP_WRITE + if newflags != obj.flags: + obj.flags = newflags # Only check for exceptions if object was either readable # or writable. flags |= select.POLLERR | select.POLLHUP | select.POLLNVAL - try: - epoll_poller.pollster.register(fd, flags) - except IOError: + if obj.poller_registered: epoll_poller.pollster.modify(fd, flags) + else: + epoll_poller.pollster.register(fd, flags) + obj.poller_registered = True try: r = epoll_poller.pollster.poll(timeout) except select.error, err: @@ -306,6 +326,8 @@ class dispatcher: closing = False addr = None ignore_log_types = frozenset(['warning']) + poller_registered = False + flags = 0 def __init__(self, sock=None, map=None): if map is None: diff --git a/src/network/http.py b/src/network/http.py index 93828c83..55cb81a1 100644 --- a/src/network/http.py +++ b/src/network/http.py @@ -19,7 +19,7 @@ class HttpConnection(AdvancedDispatcher): print "connecting in background to %s:%i" % (self.destination[0], self.destination[1]) def state_init(self): - self.write_buf += "GET %s HTTP/1.1\r\nHost: %s\r\nConnection: close\r\n\r\n" % (self.path, self.destination[0]) + self.append_write_buf("GET %s HTTP/1.1\r\nHost: %s\r\nConnection: close\r\n\r\n" % (self.path, self.destination[0])) print "Sending %ib" % (len(self.write_buf)) self.set_state("http_request_sent", 0) return False diff --git a/src/network/node.py b/src/network/node.py new file mode 100644 index 00000000..904ff4d1 --- /dev/null +++ b/src/network/node.py @@ -0,0 +1,66 @@ +import socket +import protocol + +class Node (object): + TYPE_IPV4 = 1 + TYPE_IPV6 = 2 + TYPE_ONION = 3 + TYPE_LOCAL = 4 + TYPE_LOOPBACK = 8 + TYPE_UNDEF = 12 + + def __init__(self, services, address, port): + self.services = services + self.address, self.addressType = Node.decodeIPAddress(address) + self.port = port + + def isLocal(self): + return self.addressType | Node.TYPE_LOCAL > 0 + + def isGlobal(self): + return self.addressType <= Node.TYPE_ONION + + def isOnion(self): + return self.addressType | Node.TYPE_ONION > 0 + + def isLoopback(self): + return self.addressType | Node.TYPE_LOOPBACK > 0 + + @staticmethod + def decodeIPAddress(host): + if host[0:12] == '\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xFF\xFF': + hostStandardFormat = socket.inet_ntop(socket.AF_INET, host[12:]) + return Node.decodeIPv4Address(host[12:], hostStandardFormat) + elif host[0:6] == '\xfd\x87\xd8\x7e\xeb\x43': + # Onion, based on BMD/bitcoind + hostStandardFormat = base64.b32encode(host[6:]).lower() + ".onion" + return hostStandardFormat, Node.TYPE_ONION + else: + hostStandardFormat = socket.inet_ntop(socket.AF_INET6, host) + if hostStandardFormat == "": + # This can happen on Windows systems which are not 64-bit compatible + # so let us drop the IPv6 address. + return hostStandardFormat, Node.TYPE_IPV6|Node.TYPE_UNDEF + return Node.decodeIPv6Address(host, hostStandardFormat) + + @staticmethod + def decodeIPv4Address(host, hostStandardFormat): + if host[0] == '\x7F': # 127/8 + return hostStandardFormat, Node.TYPE_IPV4|Node.TYPE_LOOPBACK + if host[0] == '\x0A': # 10/8 + return hostStandardFormat, Node.TYPE_IPV4|Node.TYPE_LOCAL + if host[0:2] == '\xC0\xA8': # 192.168/16 + return hostStandardFormat, Node.TYPE_IPV4|Node.TYPE_LOCAL + if host[0:2] >= '\xAC\x10' and host[0:2] < '\xAC\x20': # 172.16/12 + return hostStandardFormat, Node.TYPE_IPV4|Node.TYPE_LOCAL + return hostStandardFormat, Node.TYPE_IPV4 + + @staticmethod + def _checkIPv6Address(host, hostStandardFormat): + if host == ('\x00' * 15) + '\x01': + return hostStandardFormat, Node.TYPE_IPV6|Node.TYPE_LOOPBACK + if host[0] == '\xFE' and (ord(host[1]) & 0xc0) == 0x80: + return hostStandardFormat, Node.TYPE_IPV6|Node.TYPE_LOCAL + if (ord(host[0]) & 0xfe) == 0xfc: + return hostStandardFormat, Node.TYPE_IPV6|Node.TYPE_UNDEF + return hostStandardFormat, Node.TYPE_IPV6 diff --git a/src/network/socks4a.py b/src/network/socks4a.py index 091e09a5..02c8d4af 100644 --- a/src/network/socks4a.py +++ b/src/network/socks4a.py @@ -59,28 +59,28 @@ class Socks4aConnection(Socks4a): def state_auth_done(self): # Now we can request the actual connection rmtrslv = False - self.write_buf += struct.pack('>BBH', 0x04, 0x01, self.destination[1]) + self.append_write_buf(struct.pack('>BBH', 0x04, 0x01, self.destination[1])) # If the given destination address is an IP address, we'll # use the IPv4 address request even if remote resolving was specified. try: self.ipaddr = socket.inet_aton(self.destination[0]) - self.write_buf += ipaddr + self.append_write_buf(self.ipaddr) except socket.error: # Well it's not an IP number, so it's probably a DNS name. if Proxy._remote_dns: # Resolve remotely rmtrslv = True self.ipaddr = None - self.write_buf += struct.pack("BBBB", 0x00, 0x00, 0x00, 0x01) + self.append_write_buf(struct.pack("BBBB", 0x00, 0x00, 0x00, 0x01)) else: # Resolve locally self.ipaddr = socket.inet_aton(socket.gethostbyname(self.destination[0])) - self.write_buf += self.ipaddr + self.append_write_buf(self.ipaddr) if self._auth: - self.write_buf += self._auth[0] - self.write_buf += chr(0x00).encode() + self.append_write_buf(self._auth[0]) + self.append_write_buf(chr(0x00).encode()) if rmtrslv: - self.write_buf += self.destination[0] + chr(0x00).encode() + self.append_write_buf(self.destination[0] + chr(0x00).encode()) self.set_state("pre_connect", 0) @@ -92,12 +92,12 @@ class Socks4aResolver(Socks4a): def state_auth_done(self): # Now we can request the actual connection - self.write_buf += struct.pack('>BBH', 0x04, 0xF0, self.destination[1]) - self.write_buf += struct.pack("BBBB", 0x00, 0x00, 0x00, 0x01) + self.append_write_buf(struct.pack('>BBH', 0x04, 0xF0, self.destination[1])) + self.append_write_buf(struct.pack("BBBB", 0x00, 0x00, 0x00, 0x01)) if self._auth: - self.write_buf += self._auth[0] - self.write_buf += chr(0x00).encode() - self.write_buf += self.host + chr(0x00).encode() + self.append_write_buf(self._auth[0]) + self.append_write_buf(chr(0x00).encode()) + self.append_write_buf(self.host + chr(0x00).encode()) self.set_state("pre_connect", 0) def resolved(self): diff --git a/src/network/socks5.py b/src/network/socks5.py index 841c253b..5ba6f3e3 100644 --- a/src/network/socks5.py +++ b/src/network/socks5.py @@ -17,9 +17,9 @@ class Socks5(Proxy): def state_init(self): if self._auth: - self.write_buf += struct.pack('BBBB', 0x05, 0x02, 0x00, 0x02) + self.append_write_buf(struct.pack('BBBB', 0x05, 0x02, 0x00, 0x02)) else: - self.write_buf += struct.pack('BBB', 0x05, 0x01, 0x00) + self.append_write_buf(struct.pack('BBB', 0x05, 0x01, 0x00)) self.set_state("auth_1", 0) def state_auth_1(self): @@ -35,9 +35,9 @@ class Socks5(Proxy): self.set_state("auth_done", 2) elif ret[1] == 2: # username/password - self.write_buf += struct.pack('BB', 1, len(self._auth[0])) + \ + self.append_write_buf(struct.pack('BB', 1, len(self._auth[0])) + \ self._auth[0] + struct.pack('B', len(self._auth[1])) + \ - self._auth[1] + self._auth[1]) self.set_state("auth_1", 2) else: if ret[1] == 0xff: @@ -130,23 +130,23 @@ class Socks5Connection(Socks5): def state_auth_done(self): # Now we can request the actual connection - self.write_buf += struct.pack('BBB', 0x05, 0x01, 0x00) + self.append_write_buf(struct.pack('BBB', 0x05, 0x01, 0x00)) # If the given destination address is an IP address, we'll # use the IPv4 address request even if remote resolving was specified. try: self.ipaddr = socket.inet_aton(self.destination[0]) - self.write_buf += chr(0x01).encode() + self.ipaddr + self.append_write_buf(chr(0x01).encode() + self.ipaddr) except socket.error: # Well it's not an IP number, so it's probably a DNS name. if Proxy._remote_dns: # Resolve remotely self.ipaddr = None - self.write_buf += chr(0x03).encode() + chr(len(self.destination[0])).encode() + self.destination[0] + self.append_write_buf(chr(0x03).encode() + chr(len(self.destination[0])).encode() + self.destination[0]) else: # Resolve locally self.ipaddr = socket.inet_aton(socket.gethostbyname(self.destination[0])) - self.write_buf += chr(0x01).encode() + self.ipaddr - self.write_buf += struct.pack(">H", self.destination[1]) + self.append_write_buf(chr(0x01).encode() + self.ipaddr) + self.append_write_buf(struct.pack(">H", self.destination[1])) self.set_state("pre_connect", 0) @@ -158,9 +158,9 @@ class Socks5Resolver(Socks5): def state_auth_done(self): # Now we can request the actual connection - self.write_buf += struct.pack('BBB', 0x05, 0xF0, 0x00) - self.write_buf += chr(0x03).encode() + chr(len(self.host)).encode() + str(self.host) - self.write_buf += struct.pack(">H", self.port) + self.append_write_buf(struct.pack('BBB', 0x05, 0xF0, 0x00)) + self.append_write_buf(chr(0x03).encode() + chr(len(self.host)).encode() + str(self.host)) + self.append_write_buf(struct.pack(">H", self.port)) self.set_state("pre_connect", 0) def resolved(self): diff --git a/src/network/tls.py b/src/network/tls.py index 023f6cac..c7554891 100644 --- a/src/network/tls.py +++ b/src/network/tls.py @@ -2,12 +2,14 @@ SSL/TLS negotiation. """ -from network.advanceddispatcher import AdvancedDispatcher -import network.asyncore_pollchoose as asyncore +import os import socket import ssl import sys +from network.advanceddispatcher import AdvancedDispatcher +import network.asyncore_pollchoose as asyncore +import paths import protocol class TLSDispatcher(AdvancedDispatcher): @@ -30,6 +32,7 @@ class TLSDispatcher(AdvancedDispatcher): def state_tls_init(self): self.isSSL = True + self.tlsStarted = True # Once the connection has been established, it's safe to wrap the # socket. if sys.version_info >= (2,7,9): @@ -40,7 +43,7 @@ class TLSDispatcher(AdvancedDispatcher): context.verify_mode = ssl.CERT_NONE # also exclude TLSv1 and TLSv1.1 in the future context.options = ssl.OP_ALL | ssl.OP_NO_SSLv2 | ssl.OP_NO_SSLv3 | ssl.OP_SINGLE_ECDH_USE | ssl.OP_CIPHER_SERVER_PREFERENCE - self.sslSocket = context.wrap_socket(self.sock, server_side = self.server_side, do_handshake_on_connect=False) + self.sslSocket = context.wrap_socket(self.socket, server_side = self.server_side, do_handshake_on_connect=False) else: self.sslSocket = ssl.wrap_socket(self.socket, server_side=self.server_side, @@ -51,49 +54,66 @@ class TLSDispatcher(AdvancedDispatcher): do_handshake_on_connect=False) self.sslSocket.setblocking(0) self.want_read = self.want_write = True + self.set_state("tls_handshake") # if hasattr(self.socket, "context"): # self.socket.context.set_ecdh_curve("secp256k1") def writable(self): - if self.tlsStarted and not self.tlsDone: + if self.tlsStarted and not self.tlsDone and len(self.write_buf) == 0: + print "tls writable, %r" % (self.want_write) return self.want_write else: - return AdvancedDispacher.writable(self) + return AdvancedDispatcher.writable(self) def readable(self): - if self.tlsStarted and not self.tlsDone: + if self.tlsStarted and not self.tlsDone and len(self.write_buf) == 0: + print "tls readable, %r" % (self.want_read) return self.want_read else: - return AdvancedDispacher.readable(self) + return AdvancedDispatcher.readable(self) def handle_read(self): - if self.tlsStarted and not self.tlsDone: - self._handshake() + # wait for write buffer flush + if self.tlsStarted and not self.tlsDone and len(self.write_buf) == 0: + print "handshaking (read)" + self.state_tls_handshake() else: - return AdvancedDispacher.handle_read(self) + print "not handshaking (read)" + return AdvancedDispatcher.handle_read(self) def handle_write(self): - if self.tlsStarted and not not self.tlsDone: - self._handshake() + # wait for write buffer flush + if self.tlsStarted and not self.tlsDone and len(self.write_buf) == 0: + print "handshaking (write)" + self.state_tls_handshake() else: - return AdvancedDispacher.handle_write(self) + print "not handshaking (write)" + return AdvancedDispatcher.handle_write(self) def state_tls_handshake(self): - """ - Perform the handshake. - """ + # wait for flush + if len(self.write_buf) > 0: + return False + # Perform the handshake. try: + print "handshaking (internal)" self.sslSocket.do_handshake() except ssl.SSLError, err: + print "handshake fail" self.want_read = self.want_write = False if err.args[0] == ssl.SSL_ERROR_WANT_READ: + print "want read" self.want_read = True elif err.args[0] == ssl.SSL_ERROR_WANT_WRITE: + print "want write" self.want_write = True else: raise else: + print "handshake success" # The handshake has completed, so remove this channel and... self.del_channel() self.set_socket(self.sslSocket) self.tlsDone = True + self.state_bm_ready() + return False diff --git a/src/protocol.py b/src/protocol.py index 9698f917..9397cd8b 100644 --- a/src/protocol.py +++ b/src/protocol.py @@ -27,6 +27,11 @@ NODE_SSL = 2 #Bitfield flags BITFIELD_DOESACK = 1 +#Error types +STATUS_WARNING = 0 +STATUS_ERROR = 1 +STATUS_FATAL = 2 + eightBytesOfRandomDataUsedToDetectConnectionsToSelf = pack( '>Q', random.randrange(1, 18446744073709551615)) From bf76c7f6ec761eb10c4aece7068ac95f030d3ed8 Mon Sep 17 00:00:00 2001 From: Peter Surda Date: Sun, 30 Apr 2017 10:39:48 +0200 Subject: [PATCH 035/407] Allow extended encoding in API --- src/api.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/api.py b/src/api.py index d6d0b266..c8612d60 100644 --- a/src/api.py +++ b/src/api.py @@ -650,8 +650,8 @@ class MySimpleXMLRPCRequestHandler(SimpleXMLRPCRequestHandler): TTL = 4*24*60*60 elif len(params) == 6: toAddress, fromAddress, subject, message, encodingType, TTL = params - if encodingType != 2: - raise APIError(6, 'The encoding type must be 2 because that is the only one this program currently supports.') + if encodingType not in [2, 3]: + raise APIError(6, 'The encoding type must be 2 or 3.') subject = self._decode(subject, "base64") message = self._decode(message, "base64") if len(subject + message) > (2 ** 18 - 500): From 4c597d3f7cf9f83a763472aa165a1a4292019f20 Mon Sep 17 00:00:00 2001 From: Peter Surda Date: Sun, 30 Apr 2017 10:41:55 +0200 Subject: [PATCH 036/407] Error handling for non-interactive setup.py - thanks to @orlitzky #993 --- setup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.py b/setup.py index fc9e367a..98b7fbd9 100644 --- a/setup.py +++ b/setup.py @@ -184,7 +184,7 @@ class InstallCmd(install): print "Press Return to continue" try: raw_input() - except NameError: + except EOFError, NameError: pass return install.run(self) From 1c55bf7d4b0f050be13e020c996d9e4fb6b2d988 Mon Sep 17 00:00:00 2001 From: Peter Surda Date: Sun, 7 May 2017 20:15:16 +0200 Subject: [PATCH 037/407] Add umsgpack as fallback - if a "big" msgpack module isn't available, use bundled umsgpack --- src/fallback/__init__.py | 0 src/fallback/umsgpack/__init__.py | 0 src/fallback/umsgpack/umsgpack.py | 1057 +++++++++++++++++++++++++++++ src/helper_msgcoding.py | 5 +- 4 files changed, 1061 insertions(+), 1 deletion(-) create mode 100644 src/fallback/__init__.py create mode 100644 src/fallback/umsgpack/__init__.py create mode 100644 src/fallback/umsgpack/umsgpack.py diff --git a/src/fallback/__init__.py b/src/fallback/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/src/fallback/umsgpack/__init__.py b/src/fallback/umsgpack/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/src/fallback/umsgpack/umsgpack.py b/src/fallback/umsgpack/umsgpack.py new file mode 100644 index 00000000..cd7a2037 --- /dev/null +++ b/src/fallback/umsgpack/umsgpack.py @@ -0,0 +1,1057 @@ +# u-msgpack-python v2.4.1 - v at sergeev.io +# https://github.com/vsergeev/u-msgpack-python +# +# u-msgpack-python is a lightweight MessagePack serializer and deserializer +# module, compatible with both Python 2 and 3, as well CPython and PyPy +# implementations of Python. u-msgpack-python is fully compliant with the +# latest MessagePack specification.com/msgpack/msgpack/blob/master/spec.md). In +# particular, it supports the new binary, UTF-8 string, and application ext +# types. +# +# MIT License +# +# Copyright (c) 2013-2016 vsergeev / Ivan (Vanya) A. Sergeev +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +# THE SOFTWARE. +# +""" +u-msgpack-python v2.4.1 - v at sergeev.io +https://github.com/vsergeev/u-msgpack-python + +u-msgpack-python is a lightweight MessagePack serializer and deserializer +module, compatible with both Python 2 and 3, as well CPython and PyPy +implementations of Python. u-msgpack-python is fully compliant with the +latest MessagePack specification.com/msgpack/msgpack/blob/master/spec.md). In +particular, it supports the new binary, UTF-8 string, and application ext +types. + +License: MIT +""" +import struct +import collections +import sys +import io + +__version__ = "2.4.1" +"Module version string" + +version = (2, 4, 1) +"Module version tuple" + + +############################################################################## +# Ext Class +############################################################################## + +# Extension type for application-defined types and data +class Ext: + """ + The Ext class facilitates creating a serializable extension object to store + an application-defined type and data byte array. + """ + + def __init__(self, type, data): + """ + Construct a new Ext object. + + Args: + type: application-defined type integer from 0 to 127 + data: application-defined data byte array + + Raises: + TypeError: + Specified ext type is outside of 0 to 127 range. + + Example: + >>> foo = umsgpack.Ext(0x05, b"\x01\x02\x03") + >>> umsgpack.packb({u"special stuff": foo, u"awesome": True}) + '\x82\xa7awesome\xc3\xadspecial stuff\xc7\x03\x05\x01\x02\x03' + >>> bar = umsgpack.unpackb(_) + >>> print(bar["special stuff"]) + Ext Object (Type: 0x05, Data: 01 02 03) + >>> + """ + # Application ext type should be 0 <= type <= 127 + if not isinstance(type, int) or not (type >= 0 and type <= 127): + raise TypeError("ext type out of range") + # Check data is type bytes + elif sys.version_info[0] == 3 and not isinstance(data, bytes): + raise TypeError("ext data is not type \'bytes\'") + elif sys.version_info[0] == 2 and not isinstance(data, str): + raise TypeError("ext data is not type \'str\'") + self.type = type + self.data = data + + def __eq__(self, other): + """ + Compare this Ext object with another for equality. + """ + return (isinstance(other, self.__class__) and + self.type == other.type and + self.data == other.data) + + def __ne__(self, other): + """ + Compare this Ext object with another for inequality. + """ + return not self.__eq__(other) + + def __str__(self): + """ + String representation of this Ext object. + """ + s = "Ext Object (Type: 0x%02x, Data: " % self.type + s += " ".join(["0x%02x" % ord(self.data[i:i + 1]) + for i in xrange(min(len(self.data), 8))]) + if len(self.data) > 8: + s += " ..." + s += ")" + return s + + def __hash__(self): + """ + Provide a hash of this Ext object. + """ + return hash((self.type, self.data)) + + +class InvalidString(bytes): + """Subclass of bytes to hold invalid UTF-8 strings.""" + pass + +############################################################################## +# Exceptions +############################################################################## + + +# Base Exception classes +class PackException(Exception): + "Base class for exceptions encountered during packing." + pass + + +class UnpackException(Exception): + "Base class for exceptions encountered during unpacking." + pass + + +# Packing error +class UnsupportedTypeException(PackException): + "Object type not supported for packing." + pass + + +# Unpacking error +class InsufficientDataException(UnpackException): + "Insufficient data to unpack the serialized object." + pass + + +class InvalidStringException(UnpackException): + "Invalid UTF-8 string encountered during unpacking." + pass + + +class ReservedCodeException(UnpackException): + "Reserved code encountered during unpacking." + pass + + +class UnhashableKeyException(UnpackException): + """ + Unhashable key encountered during map unpacking. + The serialized map cannot be deserialized into a Python dictionary. + """ + pass + + +class DuplicateKeyException(UnpackException): + "Duplicate key encountered during map unpacking." + pass + + +# Backwards compatibility +KeyNotPrimitiveException = UnhashableKeyException +KeyDuplicateException = DuplicateKeyException + +############################################################################# +# Exported Functions and Glob +############################################################################# + +# Exported functions and variables, set up in __init() +pack = None +packb = None +unpack = None +unpackb = None +dump = None +dumps = None +load = None +loads = None + +compatibility = False +""" +Compatibility mode boolean. + +When compatibility mode is enabled, u-msgpack-python will serialize both +unicode strings and bytes into the old "raw" msgpack type, and deserialize the +"raw" msgpack type into bytes. This provides backwards compatibility with the +old MessagePack specification. + +Example: +>>> umsgpack.compatibility = True +>>> +>>> umsgpack.packb([u"some string", b"some bytes"]) +b'\x92\xabsome string\xaasome bytes' +>>> umsgpack.unpackb(_) +[b'some string', b'some bytes'] +>>> +""" + +############################################################################## +# Packing +############################################################################## + +# You may notice struct.pack("B", obj) instead of the simpler chr(obj) in the +# code below. This is to allow for seamless Python 2 and 3 compatibility, as +# chr(obj) has a str return type instead of bytes in Python 3, and +# struct.pack(...) has the right return type in both versions. + + +def _pack_integer(obj, fp, options): + if obj < 0: + if obj >= -32: + fp.write(struct.pack("b", obj)) + elif obj >= -2**(8 - 1): + fp.write(b"\xd0" + struct.pack("b", obj)) + elif obj >= -2**(16 - 1): + fp.write(b"\xd1" + struct.pack(">h", obj)) + elif obj >= -2**(32 - 1): + fp.write(b"\xd2" + struct.pack(">i", obj)) + elif obj >= -2**(64 - 1): + fp.write(b"\xd3" + struct.pack(">q", obj)) + else: + raise UnsupportedTypeException("huge signed int") + else: + if obj <= 127: + fp.write(struct.pack("B", obj)) + elif obj <= 2**8 - 1: + fp.write(b"\xcc" + struct.pack("B", obj)) + elif obj <= 2**16 - 1: + fp.write(b"\xcd" + struct.pack(">H", obj)) + elif obj <= 2**32 - 1: + fp.write(b"\xce" + struct.pack(">I", obj)) + elif obj <= 2**64 - 1: + fp.write(b"\xcf" + struct.pack(">Q", obj)) + else: + raise UnsupportedTypeException("huge unsigned int") + + +def _pack_nil(obj, fp, options): + fp.write(b"\xc0") + + +def _pack_boolean(obj, fp, options): + fp.write(b"\xc3" if obj else b"\xc2") + + +def _pack_float(obj, fp, options): + float_precision = options.get('force_float_precision', _float_precision) + + if float_precision == "double": + fp.write(b"\xcb" + struct.pack(">d", obj)) + elif float_precision == "single": + fp.write(b"\xca" + struct.pack(">f", obj)) + else: + raise ValueError("invalid float precision") + + +def _pack_string(obj, fp, options): + obj = obj.encode('utf-8') + if len(obj) <= 31: + fp.write(struct.pack("B", 0xa0 | len(obj)) + obj) + elif len(obj) <= 2**8 - 1: + fp.write(b"\xd9" + struct.pack("B", len(obj)) + obj) + elif len(obj) <= 2**16 - 1: + fp.write(b"\xda" + struct.pack(">H", len(obj)) + obj) + elif len(obj) <= 2**32 - 1: + fp.write(b"\xdb" + struct.pack(">I", len(obj)) + obj) + else: + raise UnsupportedTypeException("huge string") + + +def _pack_binary(obj, fp, options): + if len(obj) <= 2**8 - 1: + fp.write(b"\xc4" + struct.pack("B", len(obj)) + obj) + elif len(obj) <= 2**16 - 1: + fp.write(b"\xc5" + struct.pack(">H", len(obj)) + obj) + elif len(obj) <= 2**32 - 1: + fp.write(b"\xc6" + struct.pack(">I", len(obj)) + obj) + else: + raise UnsupportedTypeException("huge binary string") + + +def _pack_oldspec_raw(obj, fp, options): + if len(obj) <= 31: + fp.write(struct.pack("B", 0xa0 | len(obj)) + obj) + elif len(obj) <= 2**16 - 1: + fp.write(b"\xda" + struct.pack(">H", len(obj)) + obj) + elif len(obj) <= 2**32 - 1: + fp.write(b"\xdb" + struct.pack(">I", len(obj)) + obj) + else: + raise UnsupportedTypeException("huge raw string") + + +def _pack_ext(obj, fp, options): + if len(obj.data) == 1: + fp.write(b"\xd4" + struct.pack("B", obj.type & 0xff) + obj.data) + elif len(obj.data) == 2: + fp.write(b"\xd5" + struct.pack("B", obj.type & 0xff) + obj.data) + elif len(obj.data) == 4: + fp.write(b"\xd6" + struct.pack("B", obj.type & 0xff) + obj.data) + elif len(obj.data) == 8: + fp.write(b"\xd7" + struct.pack("B", obj.type & 0xff) + obj.data) + elif len(obj.data) == 16: + fp.write(b"\xd8" + struct.pack("B", obj.type & 0xff) + obj.data) + elif len(obj.data) <= 2**8 - 1: + fp.write(b"\xc7" + + struct.pack("BB", len(obj.data), obj.type & 0xff) + obj.data) + elif len(obj.data) <= 2**16 - 1: + fp.write(b"\xc8" + + struct.pack(">HB", len(obj.data), obj.type & 0xff) + obj.data) + elif len(obj.data) <= 2**32 - 1: + fp.write(b"\xc9" + + struct.pack(">IB", len(obj.data), obj.type & 0xff) + obj.data) + else: + raise UnsupportedTypeException("huge ext data") + + +def _pack_array(obj, fp, options): + if len(obj) <= 15: + fp.write(struct.pack("B", 0x90 | len(obj))) + elif len(obj) <= 2**16 - 1: + fp.write(b"\xdc" + struct.pack(">H", len(obj))) + elif len(obj) <= 2**32 - 1: + fp.write(b"\xdd" + struct.pack(">I", len(obj))) + else: + raise UnsupportedTypeException("huge array") + + for e in obj: + pack(e, fp, **options) + + +def _pack_map(obj, fp, options): + if len(obj) <= 15: + fp.write(struct.pack("B", 0x80 | len(obj))) + elif len(obj) <= 2**16 - 1: + fp.write(b"\xde" + struct.pack(">H", len(obj))) + elif len(obj) <= 2**32 - 1: + fp.write(b"\xdf" + struct.pack(">I", len(obj))) + else: + raise UnsupportedTypeException("huge array") + + for k, v in obj.items(): + pack(k, fp, **options) + pack(v, fp, **options) + +######################################## + + +# Pack for Python 2, with 'unicode' type, 'str' type, and 'long' type +def _pack2(obj, fp, **options): + """ + Serialize a Python object into MessagePack bytes. + + Args: + obj: a Python object + fp: a .write()-supporting file-like object + + Kwargs: + ext_handlers (dict): dictionary of Ext handlers, mapping a custom type + to a callable that packs an instance of the type + into an Ext object + force_float_precision (str): "single" to force packing floats as + IEEE-754 single-precision floats, + "double" to force packing floats as + IEEE-754 double-precision floats. + + Returns: + None. + + Raises: + UnsupportedType(PackException): + Object type not supported for packing. + + Example: + >>> f = open('test.bin', 'wb') + >>> umsgpack.pack({u"compact": True, u"schema": 0}, f) + >>> + """ + global compatibility + + ext_handlers = options.get("ext_handlers") + + if obj is None: + _pack_nil(obj, fp, options) + elif ext_handlers and obj.__class__ in ext_handlers: + _pack_ext(ext_handlers[obj.__class__](obj), fp, options) + elif isinstance(obj, bool): + _pack_boolean(obj, fp, options) + elif isinstance(obj, int) or isinstance(obj, long): + _pack_integer(obj, fp, options) + elif isinstance(obj, float): + _pack_float(obj, fp, options) + elif compatibility and isinstance(obj, unicode): + _pack_oldspec_raw(bytes(obj), fp, options) + elif compatibility and isinstance(obj, bytes): + _pack_oldspec_raw(obj, fp, options) + elif isinstance(obj, unicode): + _pack_string(obj, fp, options) + elif isinstance(obj, str): + _pack_binary(obj, fp, options) + elif isinstance(obj, list) or isinstance(obj, tuple): + _pack_array(obj, fp, options) + elif isinstance(obj, dict): + _pack_map(obj, fp, options) + elif isinstance(obj, Ext): + _pack_ext(obj, fp, options) + elif ext_handlers: + # Linear search for superclass + t = next((t for t in ext_handlers.keys() if isinstance(obj, t)), None) + if t: + _pack_ext(ext_handlers[t](obj), fp, options) + else: + raise UnsupportedTypeException( + "unsupported type: %s" % str(type(obj))) + else: + raise UnsupportedTypeException("unsupported type: %s" % str(type(obj))) + + +# Pack for Python 3, with unicode 'str' type, 'bytes' type, and no 'long' type +def _pack3(obj, fp, **options): + """ + Serialize a Python object into MessagePack bytes. + + Args: + obj: a Python object + fp: a .write()-supporting file-like object + + Kwargs: + ext_handlers (dict): dictionary of Ext handlers, mapping a custom type + to a callable that packs an instance of the type + into an Ext object + force_float_precision (str): "single" to force packing floats as + IEEE-754 single-precision floats, + "double" to force packing floats as + IEEE-754 double-precision floats. + + Returns: + None. + + Raises: + UnsupportedType(PackException): + Object type not supported for packing. + + Example: + >>> f = open('test.bin', 'wb') + >>> umsgpack.pack({u"compact": True, u"schema": 0}, f) + >>> + """ + global compatibility + + ext_handlers = options.get("ext_handlers") + + if obj is None: + _pack_nil(obj, fp, options) + elif ext_handlers and obj.__class__ in ext_handlers: + _pack_ext(ext_handlers[obj.__class__](obj), fp, options) + elif isinstance(obj, bool): + _pack_boolean(obj, fp, options) + elif isinstance(obj, int): + _pack_integer(obj, fp, options) + elif isinstance(obj, float): + _pack_float(obj, fp, options) + elif compatibility and isinstance(obj, str): + _pack_oldspec_raw(obj.encode('utf-8'), fp, options) + elif compatibility and isinstance(obj, bytes): + _pack_oldspec_raw(obj, fp, options) + elif isinstance(obj, str): + _pack_string(obj, fp, options) + elif isinstance(obj, bytes): + _pack_binary(obj, fp, options) + elif isinstance(obj, list) or isinstance(obj, tuple): + _pack_array(obj, fp, options) + elif isinstance(obj, dict): + _pack_map(obj, fp, options) + elif isinstance(obj, Ext): + _pack_ext(obj, fp, options) + elif ext_handlers: + # Linear search for superclass + t = next((t for t in ext_handlers.keys() if isinstance(obj, t)), None) + if t: + _pack_ext(ext_handlers[t](obj), fp, options) + else: + raise UnsupportedTypeException( + "unsupported type: %s" % str(type(obj))) + else: + raise UnsupportedTypeException( + "unsupported type: %s" % str(type(obj))) + + +def _packb2(obj, **options): + """ + Serialize a Python object into MessagePack bytes. + + Args: + obj: a Python object + + Kwargs: + ext_handlers (dict): dictionary of Ext handlers, mapping a custom type + to a callable that packs an instance of the type + into an Ext object + force_float_precision (str): "single" to force packing floats as + IEEE-754 single-precision floats, + "double" to force packing floats as + IEEE-754 double-precision floats. + + Returns: + A 'str' containing serialized MessagePack bytes. + + Raises: + UnsupportedType(PackException): + Object type not supported for packing. + + Example: + >>> umsgpack.packb({u"compact": True, u"schema": 0}) + '\x82\xa7compact\xc3\xa6schema\x00' + >>> + """ + fp = io.BytesIO() + _pack2(obj, fp, **options) + return fp.getvalue() + + +def _packb3(obj, **options): + """ + Serialize a Python object into MessagePack bytes. + + Args: + obj: a Python object + + Kwargs: + ext_handlers (dict): dictionary of Ext handlers, mapping a custom type + to a callable that packs an instance of the type + into an Ext object + force_float_precision (str): "single" to force packing floats as + IEEE-754 single-precision floats, + "double" to force packing floats as + IEEE-754 double-precision floats. + + Returns: + A 'bytes' containing serialized MessagePack bytes. + + Raises: + UnsupportedType(PackException): + Object type not supported for packing. + + Example: + >>> umsgpack.packb({u"compact": True, u"schema": 0}) + b'\x82\xa7compact\xc3\xa6schema\x00' + >>> + """ + fp = io.BytesIO() + _pack3(obj, fp, **options) + return fp.getvalue() + +############################################################################# +# Unpacking +############################################################################# + + +def _read_except(fp, n): + data = fp.read(n) + if len(data) < n: + raise InsufficientDataException() + return data + + +def _unpack_integer(code, fp, options): + if (ord(code) & 0xe0) == 0xe0: + return struct.unpack("b", code)[0] + elif code == b'\xd0': + return struct.unpack("b", _read_except(fp, 1))[0] + elif code == b'\xd1': + return struct.unpack(">h", _read_except(fp, 2))[0] + elif code == b'\xd2': + return struct.unpack(">i", _read_except(fp, 4))[0] + elif code == b'\xd3': + return struct.unpack(">q", _read_except(fp, 8))[0] + elif (ord(code) & 0x80) == 0x00: + return struct.unpack("B", code)[0] + elif code == b'\xcc': + return struct.unpack("B", _read_except(fp, 1))[0] + elif code == b'\xcd': + return struct.unpack(">H", _read_except(fp, 2))[0] + elif code == b'\xce': + return struct.unpack(">I", _read_except(fp, 4))[0] + elif code == b'\xcf': + return struct.unpack(">Q", _read_except(fp, 8))[0] + raise Exception("logic error, not int: 0x%02x" % ord(code)) + + +def _unpack_reserved(code, fp, options): + if code == b'\xc1': + raise ReservedCodeException( + "encountered reserved code: 0x%02x" % ord(code)) + raise Exception( + "logic error, not reserved code: 0x%02x" % ord(code)) + + +def _unpack_nil(code, fp, options): + if code == b'\xc0': + return None + raise Exception("logic error, not nil: 0x%02x" % ord(code)) + + +def _unpack_boolean(code, fp, options): + if code == b'\xc2': + return False + elif code == b'\xc3': + return True + raise Exception("logic error, not boolean: 0x%02x" % ord(code)) + + +def _unpack_float(code, fp, options): + if code == b'\xca': + return struct.unpack(">f", _read_except(fp, 4))[0] + elif code == b'\xcb': + return struct.unpack(">d", _read_except(fp, 8))[0] + raise Exception("logic error, not float: 0x%02x" % ord(code)) + + +def _unpack_string(code, fp, options): + if (ord(code) & 0xe0) == 0xa0: + length = ord(code) & ~0xe0 + elif code == b'\xd9': + length = struct.unpack("B", _read_except(fp, 1))[0] + elif code == b'\xda': + length = struct.unpack(">H", _read_except(fp, 2))[0] + elif code == b'\xdb': + length = struct.unpack(">I", _read_except(fp, 4))[0] + else: + raise Exception("logic error, not string: 0x%02x" % ord(code)) + + # Always return raw bytes in compatibility mode + global compatibility + if compatibility: + return _read_except(fp, length) + + data = _read_except(fp, length) + try: + return bytes.decode(data, 'utf-8') + except UnicodeDecodeError: + if options.get("allow_invalid_utf8"): + return InvalidString(data) + raise InvalidStringException("unpacked string is invalid utf-8") + + +def _unpack_binary(code, fp, options): + if code == b'\xc4': + length = struct.unpack("B", _read_except(fp, 1))[0] + elif code == b'\xc5': + length = struct.unpack(">H", _read_except(fp, 2))[0] + elif code == b'\xc6': + length = struct.unpack(">I", _read_except(fp, 4))[0] + else: + raise Exception("logic error, not binary: 0x%02x" % ord(code)) + + return _read_except(fp, length) + + +def _unpack_ext(code, fp, options): + if code == b'\xd4': + length = 1 + elif code == b'\xd5': + length = 2 + elif code == b'\xd6': + length = 4 + elif code == b'\xd7': + length = 8 + elif code == b'\xd8': + length = 16 + elif code == b'\xc7': + length = struct.unpack("B", _read_except(fp, 1))[0] + elif code == b'\xc8': + length = struct.unpack(">H", _read_except(fp, 2))[0] + elif code == b'\xc9': + length = struct.unpack(">I", _read_except(fp, 4))[0] + else: + raise Exception("logic error, not ext: 0x%02x" % ord(code)) + + ext = Ext(ord(_read_except(fp, 1)), _read_except(fp, length)) + + # Unpack with ext handler, if we have one + ext_handlers = options.get("ext_handlers") + if ext_handlers and ext.type in ext_handlers: + ext = ext_handlers[ext.type](ext) + + return ext + + +def _unpack_array(code, fp, options): + if (ord(code) & 0xf0) == 0x90: + length = (ord(code) & ~0xf0) + elif code == b'\xdc': + length = struct.unpack(">H", _read_except(fp, 2))[0] + elif code == b'\xdd': + length = struct.unpack(">I", _read_except(fp, 4))[0] + else: + raise Exception("logic error, not array: 0x%02x" % ord(code)) + + return [_unpack(fp, options) for i in xrange(length)] + + +def _deep_list_to_tuple(obj): + if isinstance(obj, list): + return tuple([_deep_list_to_tuple(e) for e in obj]) + return obj + + +def _unpack_map(code, fp, options): + if (ord(code) & 0xf0) == 0x80: + length = (ord(code) & ~0xf0) + elif code == b'\xde': + length = struct.unpack(">H", _read_except(fp, 2))[0] + elif code == b'\xdf': + length = struct.unpack(">I", _read_except(fp, 4))[0] + else: + raise Exception("logic error, not map: 0x%02x" % ord(code)) + + d = {} if not options.get('use_ordered_dict') \ + else collections.OrderedDict() + for _ in xrange(length): + # Unpack key + k = _unpack(fp, options) + + if isinstance(k, list): + # Attempt to convert list into a hashable tuple + k = _deep_list_to_tuple(k) + elif not isinstance(k, collections.Hashable): + raise UnhashableKeyException( + "encountered unhashable key: %s, %s" % (str(k), str(type(k)))) + elif k in d: + raise DuplicateKeyException( + "encountered duplicate key: %s, %s" % (str(k), str(type(k)))) + + # Unpack value + v = _unpack(fp, options) + + try: + d[k] = v + except TypeError: + raise UnhashableKeyException( + "encountered unhashable key: %s" % str(k)) + return d + + +def _unpack(fp, options): + code = _read_except(fp, 1) + return _unpack_dispatch_table[code](code, fp, options) + +######################################## + + +def _unpack2(fp, **options): + """ + Deserialize MessagePack bytes into a Python object. + + Args: + fp: a .read()-supporting file-like object + + Kwargs: + ext_handlers (dict): dictionary of Ext handlers, mapping integer Ext + type to a callable that unpacks an instance of + Ext into an object + use_ordered_dict (bool): unpack maps into OrderedDict, instead of + unordered dict (default False) + allow_invalid_utf8 (bool): unpack invalid strings into instances of + InvalidString, for access to the bytes + (default False) + + Returns: + A Python object. + + Raises: + InsufficientDataException(UnpackException): + Insufficient data to unpack the serialized object. + InvalidStringException(UnpackException): + Invalid UTF-8 string encountered during unpacking. + ReservedCodeException(UnpackException): + Reserved code encountered during unpacking. + UnhashableKeyException(UnpackException): + Unhashable key encountered during map unpacking. + The serialized map cannot be deserialized into a Python dictionary. + DuplicateKeyException(UnpackException): + Duplicate key encountered during map unpacking. + + Example: + >>> f = open('test.bin', 'rb') + >>> umsgpack.unpackb(f) + {u'compact': True, u'schema': 0} + >>> + """ + return _unpack(fp, options) + + +def _unpack3(fp, **options): + """ + Deserialize MessagePack bytes into a Python object. + + Args: + fp: a .read()-supporting file-like object + + Kwargs: + ext_handlers (dict): dictionary of Ext handlers, mapping integer Ext + type to a callable that unpacks an instance of + Ext into an object + use_ordered_dict (bool): unpack maps into OrderedDict, instead of + unordered dict (default False) + allow_invalid_utf8 (bool): unpack invalid strings into instances of + InvalidString, for access to the bytes + (default False) + + Returns: + A Python object. + + Raises: + InsufficientDataException(UnpackException): + Insufficient data to unpack the serialized object. + InvalidStringException(UnpackException): + Invalid UTF-8 string encountered during unpacking. + ReservedCodeException(UnpackException): + Reserved code encountered during unpacking. + UnhashableKeyException(UnpackException): + Unhashable key encountered during map unpacking. + The serialized map cannot be deserialized into a Python dictionary. + DuplicateKeyException(UnpackException): + Duplicate key encountered during map unpacking. + + Example: + >>> f = open('test.bin', 'rb') + >>> umsgpack.unpackb(f) + {'compact': True, 'schema': 0} + >>> + """ + return _unpack(fp, options) + + +# For Python 2, expects a str object +def _unpackb2(s, **options): + """ + Deserialize MessagePack bytes into a Python object. + + Args: + s: a 'str' or 'bytearray' containing serialized MessagePack bytes + + Kwargs: + ext_handlers (dict): dictionary of Ext handlers, mapping integer Ext + type to a callable that unpacks an instance of + Ext into an object + use_ordered_dict (bool): unpack maps into OrderedDict, instead of + unordered dict (default False) + allow_invalid_utf8 (bool): unpack invalid strings into instances of + InvalidString, for access to the bytes + (default False) + + Returns: + A Python object. + + Raises: + TypeError: + Packed data type is neither 'str' nor 'bytearray'. + InsufficientDataException(UnpackException): + Insufficient data to unpack the serialized object. + InvalidStringException(UnpackException): + Invalid UTF-8 string encountered during unpacking. + ReservedCodeException(UnpackException): + Reserved code encountered during unpacking. + UnhashableKeyException(UnpackException): + Unhashable key encountered during map unpacking. + The serialized map cannot be deserialized into a Python dictionary. + DuplicateKeyException(UnpackException): + Duplicate key encountered during map unpacking. + + Example: + >>> umsgpack.unpackb(b'\x82\xa7compact\xc3\xa6schema\x00') + {u'compact': True, u'schema': 0} + >>> + """ + if not isinstance(s, (str, bytearray)): + raise TypeError("packed data must be type 'str' or 'bytearray'") + return _unpack(io.BytesIO(s), options) + + +# For Python 3, expects a bytes object +def _unpackb3(s, **options): + """ + Deserialize MessagePack bytes into a Python object. + + Args: + s: a 'bytes' or 'bytearray' containing serialized MessagePack bytes + + Kwargs: + ext_handlers (dict): dictionary of Ext handlers, mapping integer Ext + type to a callable that unpacks an instance of + Ext into an object + use_ordered_dict (bool): unpack maps into OrderedDict, instead of + unordered dict (default False) + allow_invalid_utf8 (bool): unpack invalid strings into instances of + InvalidString, for access to the bytes + (default False) + + Returns: + A Python object. + + Raises: + TypeError: + Packed data type is neither 'bytes' nor 'bytearray'. + InsufficientDataException(UnpackException): + Insufficient data to unpack the serialized object. + InvalidStringException(UnpackException): + Invalid UTF-8 string encountered during unpacking. + ReservedCodeException(UnpackException): + Reserved code encountered during unpacking. + UnhashableKeyException(UnpackException): + Unhashable key encountered during map unpacking. + The serialized map cannot be deserialized into a Python dictionary. + DuplicateKeyException(UnpackException): + Duplicate key encountered during map unpacking. + + Example: + >>> umsgpack.unpackb(b'\x82\xa7compact\xc3\xa6schema\x00') + {'compact': True, 'schema': 0} + >>> + """ + if not isinstance(s, (bytes, bytearray)): + raise TypeError("packed data must be type 'bytes' or 'bytearray'") + return _unpack(io.BytesIO(s), options) + +############################################################################# +# Module Initialization +############################################################################# + + +def __init(): + global pack + global packb + global unpack + global unpackb + global dump + global dumps + global load + global loads + global compatibility + global _float_precision + global _unpack_dispatch_table + global xrange + + # Compatibility mode for handling strings/bytes with the old specification + compatibility = False + + # Auto-detect system float precision + if sys.float_info.mant_dig == 53: + _float_precision = "double" + else: + _float_precision = "single" + + # Map packb and unpackb to the appropriate version + if sys.version_info[0] == 3: + pack = _pack3 + packb = _packb3 + dump = _pack3 + dumps = _packb3 + unpack = _unpack3 + unpackb = _unpackb3 + load = _unpack3 + loads = _unpackb3 + xrange = range + else: + pack = _pack2 + packb = _packb2 + dump = _pack2 + dumps = _packb2 + unpack = _unpack2 + unpackb = _unpackb2 + load = _unpack2 + loads = _unpackb2 + + # Build a dispatch table for fast lookup of unpacking function + + _unpack_dispatch_table = {} + # Fix uint + for code in range(0, 0x7f + 1): + _unpack_dispatch_table[struct.pack("B", code)] = _unpack_integer + # Fix map + for code in range(0x80, 0x8f + 1): + _unpack_dispatch_table[struct.pack("B", code)] = _unpack_map + # Fix array + for code in range(0x90, 0x9f + 1): + _unpack_dispatch_table[struct.pack("B", code)] = _unpack_array + # Fix str + for code in range(0xa0, 0xbf + 1): + _unpack_dispatch_table[struct.pack("B", code)] = _unpack_string + # Nil + _unpack_dispatch_table[b'\xc0'] = _unpack_nil + # Reserved + _unpack_dispatch_table[b'\xc1'] = _unpack_reserved + # Boolean + _unpack_dispatch_table[b'\xc2'] = _unpack_boolean + _unpack_dispatch_table[b'\xc3'] = _unpack_boolean + # Bin + for code in range(0xc4, 0xc6 + 1): + _unpack_dispatch_table[struct.pack("B", code)] = _unpack_binary + # Ext + for code in range(0xc7, 0xc9 + 1): + _unpack_dispatch_table[struct.pack("B", code)] = _unpack_ext + # Float + _unpack_dispatch_table[b'\xca'] = _unpack_float + _unpack_dispatch_table[b'\xcb'] = _unpack_float + # Uint + for code in range(0xcc, 0xcf + 1): + _unpack_dispatch_table[struct.pack("B", code)] = _unpack_integer + # Int + for code in range(0xd0, 0xd3 + 1): + _unpack_dispatch_table[struct.pack("B", code)] = _unpack_integer + # Fixext + for code in range(0xd4, 0xd8 + 1): + _unpack_dispatch_table[struct.pack("B", code)] = _unpack_ext + # String + for code in range(0xd9, 0xdb + 1): + _unpack_dispatch_table[struct.pack("B", code)] = _unpack_string + # Array + _unpack_dispatch_table[b'\xdc'] = _unpack_array + _unpack_dispatch_table[b'\xdd'] = _unpack_array + # Map + _unpack_dispatch_table[b'\xde'] = _unpack_map + _unpack_dispatch_table[b'\xdf'] = _unpack_map + # Negative fixint + for code in range(0xe0, 0xff + 1): + _unpack_dispatch_table[struct.pack("B", code)] = _unpack_integer + + +__init() diff --git a/src/helper_msgcoding.py b/src/helper_msgcoding.py index 2ae44eea..2acdd6a4 100644 --- a/src/helper_msgcoding.py +++ b/src/helper_msgcoding.py @@ -1,6 +1,9 @@ #!/usr/bin/python2.7 -import msgpack +try: + import msgpack +except ImportError: + import fallback.umsgpack as msgpack import string import zlib From 23b955592999038ab552e1f0444960a3773d65f1 Mon Sep 17 00:00:00 2001 From: Peter Surda Date: Sun, 7 May 2017 20:15:57 +0200 Subject: [PATCH 038/407] Add TLS version debug info --- src/class_receiveDataThread.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/class_receiveDataThread.py b/src/class_receiveDataThread.py index aeb38e78..4e86196c 100644 --- a/src/class_receiveDataThread.py +++ b/src/class_receiveDataThread.py @@ -290,6 +290,8 @@ class receiveDataThread(threading.Thread): try: self.sslSock.do_handshake() logger.debug("TLS handshake success") + if sys.version_info >= (2, 7, 9): + logger.debug("TLS protocol version: %s", self.sslSock.version()) break except ssl.SSLError as e: if sys.hexversion >= 0x02070900: From d9d3515905a95852a7463e4c954c2449b91a06da Mon Sep 17 00:00:00 2001 From: Peter Surda Date: Sun, 7 May 2017 20:16:49 +0200 Subject: [PATCH 039/407] Node class, WIP - for new network subsystem --- src/network/node.py | 114 ++++++++++++++++++++++---------------------- 1 file changed, 57 insertions(+), 57 deletions(-) diff --git a/src/network/node.py b/src/network/node.py index 904ff4d1..054d07d8 100644 --- a/src/network/node.py +++ b/src/network/node.py @@ -1,66 +1,66 @@ -import socket -import protocol +import time -class Node (object): - TYPE_IPV4 = 1 - TYPE_IPV6 = 2 - TYPE_ONION = 3 - TYPE_LOCAL = 4 - TYPE_LOOPBACK = 8 - TYPE_UNDEF = 12 +from inventory import PendingDownloadQueue - def __init__(self, services, address, port): - self.services = services - self.address, self.addressType = Node.decodeIPAddress(address) - self.port = port +try: + # pybloomfiltermmap + from pybloomfilter import BloomFilter +except ImportError: + try: + # pybloom + from pybloom import BloomFilter + except ImportError: + # bundled pybloom + from fallback.pybloom import BloomFilter - def isLocal(self): - return self.addressType | Node.TYPE_LOCAL > 0 - def isGlobal(self): - return self.addressType <= Node.TYPE_ONION +class Node(object): + invCleanPeriod = 300 + invInitialCapacity = 50000 + invErrorRate = 0.03 - def isOnion(self): - return self.addressType | Node.TYPE_ONION > 0 + def __init__(self): + self.initInvBloom() + self.initAddrBloom() - def isLoopback(self): - return self.addressType | Node.TYPE_LOOPBACK > 0 + def initInvBloom(self): + # lock? + self.invBloom = BloomFilter(capacity=Node.invInitialCapacity, + error_rate=Node.invErrorRate) - @staticmethod - def decodeIPAddress(host): - if host[0:12] == '\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xFF\xFF': - hostStandardFormat = socket.inet_ntop(socket.AF_INET, host[12:]) - return Node.decodeIPv4Address(host[12:], hostStandardFormat) - elif host[0:6] == '\xfd\x87\xd8\x7e\xeb\x43': - # Onion, based on BMD/bitcoind - hostStandardFormat = base64.b32encode(host[6:]).lower() + ".onion" - return hostStandardFormat, Node.TYPE_ONION - else: - hostStandardFormat = socket.inet_ntop(socket.AF_INET6, host) - if hostStandardFormat == "": - # This can happen on Windows systems which are not 64-bit compatible - # so let us drop the IPv6 address. - return hostStandardFormat, Node.TYPE_IPV6|Node.TYPE_UNDEF - return Node.decodeIPv6Address(host, hostStandardFormat) + def initAddrBloom(self): + # lock? + self.addrBloom = BloomFilter(capacity=Node.invInitialCapacity, + error_rate=Node.invErrorRate) - @staticmethod - def decodeIPv4Address(host, hostStandardFormat): - if host[0] == '\x7F': # 127/8 - return hostStandardFormat, Node.TYPE_IPV4|Node.TYPE_LOOPBACK - if host[0] == '\x0A': # 10/8 - return hostStandardFormat, Node.TYPE_IPV4|Node.TYPE_LOCAL - if host[0:2] == '\xC0\xA8': # 192.168/16 - return hostStandardFormat, Node.TYPE_IPV4|Node.TYPE_LOCAL - if host[0:2] >= '\xAC\x10' and host[0:2] < '\xAC\x20': # 172.16/12 - return hostStandardFormat, Node.TYPE_IPV4|Node.TYPE_LOCAL - return hostStandardFormat, Node.TYPE_IPV4 + def cleanBloom(self): + if self.lastcleaned < time.time() - Node.invCleanPeriod: + if PendingDownloadQueue().size() == 0: + self.initInvBloom() + self.initAddrBloom() + + def hasInv(self, hashid): + return hashid in self.invBloom + + def addInv(self, hashid): + self.invBloom.add(hashid) + + def hasAddr(self, hashid): + return hashid in self.invBloom + + def addInv(self, hashid): + self.invBloom.add(hashid) + +# addr sending -> per node upload queue, and flush every minute or so +# inv sending -> if not in bloom, inv immediately, otherwise put into a per node upload queue and flush every minute or so + +# no bloom +# - if inv arrives +# - if we don't have it, add tracking and download queue +# - if we do have it, remove from tracking +# tracking downloads +# - per node hash of items the node has but we don't +# tracking inv +# - per node hash of items that neither the remote node nor we have +# - @staticmethod - def _checkIPv6Address(host, hostStandardFormat): - if host == ('\x00' * 15) + '\x01': - return hostStandardFormat, Node.TYPE_IPV6|Node.TYPE_LOOPBACK - if host[0] == '\xFE' and (ord(host[1]) & 0xc0) == 0x80: - return hostStandardFormat, Node.TYPE_IPV6|Node.TYPE_LOCAL - if (ord(host[0]) & 0xfe) == 0xfc: - return hostStandardFormat, Node.TYPE_IPV6|Node.TYPE_UNDEF - return hostStandardFormat, Node.TYPE_IPV6 From 90ef2d54e1b25f4d82f3c32390d5e65e93599f79 Mon Sep 17 00:00:00 2001 From: anryko Date: Tue, 9 May 2017 21:57:52 +0200 Subject: [PATCH 040/407] Fixed INSTALL.md markdown. --- INSTALL.md | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/INSTALL.md b/INSTALL.md index 823608fe..19a22f3d 100644 --- a/INSTALL.md +++ b/INSTALL.md @@ -1,4 +1,4 @@ -#PyBitmessage Installation Instructions +# PyBitmessage Installation Instructions For an up-to-date version of these instructions, please visit the [Bitmessage Wiki](https://bitmessage.org/wiki/Compiling_instructions). @@ -6,7 +6,7 @@ For an up-to-date version of these instructions, please visit the PyBitmessage can be run either straight from source or from an installed package. -##Dependencies +## Dependencies Before running PyBitmessage, make sure you have all the necessary dependencies installed on your system. @@ -16,12 +16,12 @@ Here's a list of dependencies needed for PyBitmessage - openssl - (Fedora & Redhat only) openssl-compat-bitcoin-libs -##Running PyBitmessage +## Running PyBitmessage PyBitmessage can be run two ways: straight from source or via a package which is installed on your system. Since PyBitmessage is Beta, it is best to run PyBitmessage from source, so that you may update as needed. -####Updating +#### Updating To update PyBitmessage from source (Linux/OS X), you can do these easy steps: ``` cd PyBitmessage/src/ @@ -31,7 +31,7 @@ python bitmessagemain.py ``` Voilà! Bitmessage is updated! -####Linux +#### Linux To run PyBitmessage from the command-line, you must download the source, then run `src/bitmessagemain.py`. ``` @@ -41,7 +41,7 @@ cd PyBitmessage/ && python src/bitmessagemain.py That's it! *Honestly*! -####Windows +#### Windows On Windows you can download an executable for Bitmessage [here](https://bitmessage.org/download/windows/Bitmessage.exe). @@ -49,7 +49,7 @@ However, if you would like to run PyBitmessage via Python in Windows, you can go [here](https://bitmessage.org/wiki/Compiling_instructions#Windows) for information on how to do so. -####OS X +#### OS X First off, install Homebrew. ``` ruby -e "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install)" @@ -66,13 +66,12 @@ git clone git://github.com/Bitmessage/PyBitmessage.git cd PyBitmessage && python src/bitmessagemain.py ``` -##Creating a package for installation +## Creating a package for installation If you really want, you can make a package for PyBitmessage, which you may install yourself or distribute to friends. This isn't recommended, since PyBitmessage is in Beta, and subject to frequent change. -####Linux - +#### Linux First off, since PyBitmessage uses something nifty called [packagemonkey](https://github.com/fuzzgun/packagemonkey), go ahead and get that installed. You may have to build it from source. @@ -90,11 +89,12 @@ rpm.sh - create a RPM package slack.sh - create a package for Slackware ``` -####OS X +#### OS X Please refer to [this page](https://bitmessage.org/forum/index.php/topic,2761.0.html) on the forums for instructions on how to create a package on OS X. Please note that some versions of OS X don't work. -###Windows -#TODO: Create Windows package creation instructions + +#### Windows +## TODO: Create Windows package creation instructions From fd2603247df24c5b35c496a3a2f5d7ad2e052334 Mon Sep 17 00:00:00 2001 From: Peter Surda Date: Wed, 10 May 2017 20:01:23 +0200 Subject: [PATCH 041/407] Fix onionbindip for some systems with IPv6 - in some cases when IPv6 stack is available and onionbindip is an IPv4 address, socket.bind doesn't change the bound address, ending up listening on everything --- src/class_singleListener.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/class_singleListener.py b/src/class_singleListener.py index 243a494a..7626542d 100644 --- a/src/class_singleListener.py +++ b/src/class_singleListener.py @@ -33,6 +33,10 @@ class singleListener(threading.Thread, StoppableThread): HOST = '' # Symbolic name meaning all available interfaces # If not sockslisten, but onionhostname defined, only listen on localhost if not BMConfigParser().safeGetBoolean('bitmessagesettings', 'sockslisten') and ".onion" in BMConfigParser().get('bitmessagesettings', 'onionhostname'): + if family == socket.AF_INET6 and "." in BMConfigParser().get('bitmessagesettings', 'onionbindip'): + raise socket.error(errno.EINVAL, "Invalid mix of IPv4 and IPv6") + elif family == socket.AF_INET and ":" in BMConfigParser().get('bitmessagesettings', 'onionbindip'): + raise socket.error(errno.EINVAL, "Invalid mix of IPv4 and IPv6") HOST = BMConfigParser().get('bitmessagesettings', 'onionbindip') PORT = BMConfigParser().getint('bitmessagesettings', 'port') sock = socket.socket(family, socket.SOCK_STREAM) From 82c3c111b7acf64cacc7834042fd2233cbc3f9a3 Mon Sep 17 00:00:00 2001 From: Peter Surda Date: Fri, 12 May 2017 14:39:25 +0200 Subject: [PATCH 042/407] Fix os-release open mode - thanks to @Lvl4Sword for reporting --- setup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.py b/setup.py index 98b7fbd9..a04c51d4 100644 --- a/setup.py +++ b/setup.py @@ -100,7 +100,7 @@ def detectOS(): elif sys.platform.startswith('win'): detectOS.result = "Windows" elif os.path.isfile("/etc/os-release"): - with open("/etc/os-release", 'rt') as osRelease: + with open("/etc/os-release", 'r') as osRelease: version = None for line in osRelease: if line.startswith("NAME="): From 660997f8e77e192abf56230e062c70cc3a154b91 Mon Sep 17 00:00:00 2001 From: Peter Surda Date: Sun, 14 May 2017 15:40:35 +0200 Subject: [PATCH 043/407] Quick hack for excessively long messages - only process the first MB of a message for GUI purposes (parsing/rendering) --- src/bitmessageqt/messageview.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/bitmessageqt/messageview.py b/src/bitmessageqt/messageview.py index 40830a70..882dbb81 100644 --- a/src/bitmessageqt/messageview.py +++ b/src/bitmessageqt/messageview.py @@ -128,6 +128,7 @@ class MessageView(QtGui.QTextBrowser): self.html.reset() self.html.reset_safe() self.html.allow_picture = True - self.html.feed(data) + # quick hack to limit excessively compressed messages, limits viewing to first MB of data + self.html.feed(data[0:1048576]) self.html.close() self.showPlain() From 9f4a1fa0a4792d3e50c5915fdd3079207ba158a0 Mon Sep 17 00:00:00 2001 From: Peter Surda Date: Mon, 15 May 2017 12:18:07 +0200 Subject: [PATCH 044/407] Config file defaults and address unification - bmconfigpaser.py now allows to put default values for a specific option in the file - addresses as sections are now detected by "BM-" rather than just ignoring bitmessagesettings. There can now be other sections with a cleaner config file --- src/api.py | 30 ++++++++++----------- src/bitmessagecurses/__init__.py | 25 +++++++++-------- src/bitmessageqt/account.py | 2 +- src/bmconfigparser.py | 46 +++++++++++++++++++++----------- src/class_singleWorker.py | 2 +- src/class_smtpDeliver.py | 2 +- src/class_smtpServer.py | 4 +-- src/class_sqlThread.py | 2 +- src/shared.py | 38 +++++++++++++------------- 9 files changed, 81 insertions(+), 70 deletions(-) diff --git a/src/api.py b/src/api.py index c8612d60..82a56d40 100644 --- a/src/api.py +++ b/src/api.py @@ -169,22 +169,20 @@ class MySimpleXMLRPCRequestHandler(SimpleXMLRPCRequestHandler): def HandleListAddresses(self, method): data = '{"addresses":[' - configSections = BMConfigParser().sections() - for addressInKeysFile in configSections: - if addressInKeysFile != 'bitmessagesettings': - status, addressVersionNumber, streamNumber, hash01 = decodeAddress( - addressInKeysFile) - if len(data) > 20: - data += ',' - if BMConfigParser().has_option(addressInKeysFile, 'chan'): - chan = BMConfigParser().getboolean(addressInKeysFile, 'chan') - else: - chan = False - label = BMConfigParser().get(addressInKeysFile, 'label') - if method == 'listAddresses2': - label = label.encode('base64') - data += json.dumps({'label': label, 'address': addressInKeysFile, 'stream': - streamNumber, 'enabled': BMConfigParser().getboolean(addressInKeysFile, 'enabled'), 'chan': chan}, indent=4, separators=(',', ': ')) + for addressInKeysFile in BMConfigParser().addresses(): + status, addressVersionNumber, streamNumber, hash01 = decodeAddress( + addressInKeysFile) + if len(data) > 20: + data += ',' + if BMConfigParser().has_option(addressInKeysFile, 'chan'): + chan = BMConfigParser().getboolean(addressInKeysFile, 'chan') + else: + chan = False + label = BMConfigParser().get(addressInKeysFile, 'label') + if method == 'listAddresses2': + label = label.encode('base64') + data += json.dumps({'label': label, 'address': addressInKeysFile, 'stream': + streamNumber, 'enabled': BMConfigParser().getboolean(addressInKeysFile, 'enabled'), 'chan': chan}, indent=4, separators=(',', ': ')) data += ']}' return data diff --git a/src/bitmessagecurses/__init__.py b/src/bitmessagecurses/__init__.py index d710720f..903b7e68 100644 --- a/src/bitmessagecurses/__init__.py +++ b/src/bitmessagecurses/__init__.py @@ -1024,20 +1024,19 @@ def run(stdscr): curses.init_pair(9, curses.COLOR_YELLOW, curses.COLOR_BLACK) # orangish # Init list of address in 'Your Identities' tab - configSections = BMConfigParser().sections() + configSections = BMConfigParser().addressses() for addressInKeysFile in configSections: - if addressInKeysFile != "bitmessagesettings": - isEnabled = BMConfigParser().getboolean(addressInKeysFile, "enabled") - addresses.append([BMConfigParser().get(addressInKeysFile, "label"), isEnabled, addressInKeysFile]) - # Set address color - if not isEnabled: - addresses[len(addresses)-1].append(8) # gray - elif BMConfigParser().safeGetBoolean(addressInKeysFile, 'chan'): - addresses[len(addresses)-1].append(9) # orange - elif BMConfigParser().safeGetBoolean(addressInKeysFile, 'mailinglist'): - addresses[len(addresses)-1].append(5) # magenta - else: - addresses[len(addresses)-1].append(0) # black + isEnabled = BMConfigParser().getboolean(addressInKeysFile, "enabled") + addresses.append([BMConfigParser().get(addressInKeysFile, "label"), isEnabled, addressInKeysFile]) + # Set address color + if not isEnabled: + addresses[len(addresses)-1].append(8) # gray + elif BMConfigParser().safeGetBoolean(addressInKeysFile, 'chan'): + addresses[len(addresses)-1].append(9) # orange + elif BMConfigParser().safeGetBoolean(addressInKeysFile, 'mailinglist'): + addresses[len(addresses)-1].append(5) # magenta + else: + addresses[len(addresses)-1].append(0) # black addresses.reverse() stdscr.clear() diff --git a/src/bitmessageqt/account.py b/src/bitmessageqt/account.py index 611f5039..eee6c7b4 100644 --- a/src/bitmessageqt/account.py +++ b/src/bitmessageqt/account.py @@ -13,7 +13,7 @@ from utils import str_broadcast_subscribers import time def getSortedAccounts(): - configSections = filter(lambda x: x != 'bitmessagesettings', BMConfigParser().sections()) + configSections = BMConfigParser().addresses() configSections.sort(cmp = lambda x,y: cmp(unicode(BMConfigParser().get(x, 'label'), 'utf-8').lower(), unicode(BMConfigParser().get(y, 'label'), 'utf-8').lower()) ) diff --git a/src/bmconfigparser.py b/src/bmconfigparser.py index 412d2fa5..448a9ffc 100644 --- a/src/bmconfigparser.py +++ b/src/bmconfigparser.py @@ -6,6 +6,17 @@ import os from singleton import Singleton import state +BMConfigDefaults = { + "bitmessagesettings": { + "maxaddrperstreamsend": 500, + "maxbootstrapconnections": 20, + "maxoutboundconnections": 8, + "maxtotalconnections": 200, + }, + "zlib": { + 'maxsize': 1048576 + } +} @Singleton class BMConfigParser(ConfigParser.SafeConfigParser): @@ -21,33 +32,38 @@ class BMConfigParser(ConfigParser.SafeConfigParser): return ConfigParser.ConfigParser.get(self, section, option, raw, vars) except ConfigParser.InterpolationError: return ConfigParser.ConfigParser.get(self, section, option, True, vars) - return ConfigParser.ConfigParser.get(self, section, option, True, vars) + try: + return ConfigParser.ConfigParser.get(self, section, option, True, vars) + except (ConfigParser.NoSectionError, ConfigParser.NoOptionError) as e: + try: + return BMConfigDefaults[section][option] + except KeyError: + raise e def safeGetBoolean(self, section, field): - if self.has_option(section, field): - try: - return self.getboolean(section, field) - except ValueError: - return False - return False + try: + return self.getboolean(section, field) + except (ConfigParser.NoSectionError, ConfigParser.NoOptionError, ValueError): + return False def safeGetInt(self, section, field, default=0): - if self.has_option(section, field): - try: - return self.getint(section, field) - except ValueError: - return default - return default + try: + return self.getint(section, field) + except (ConfigParser.NoSectionError, ConfigParser.NoOptionError, ValueError): + return default def safeGet(self, section, option, default = None): - if self.has_option(section, option): + try: return self.get(section, option) - else: + except (ConfigParser.NoSectionError, ConfigParser.NoOptionError, ValueError): return default def items(self, section, raw=False, vars=None): return ConfigParser.ConfigParser.items(self, section, True, vars) + def addresses(self): + return filter(lambda x: x.startswith('BM-'), BMConfigParser().sections()) + def save(self): fileName = os.path.join(state.appdata, 'keys.dat') fileNameBak = fileName + "." + datetime.datetime.now().strftime("%Y%j%H%M%S%f") + '.bak' diff --git a/src/class_singleWorker.py b/src/class_singleWorker.py index b45c4aca..c2d16de4 100644 --- a/src/class_singleWorker.py +++ b/src/class_singleWorker.py @@ -136,7 +136,7 @@ class singleWorker(threading.Thread, StoppableThread): def doPOWForMyV2Pubkey(self, hash): # This function also broadcasts out the pubkey message once it is done with the POW # Look up my stream number based on my address hash - """configSections = shared.config.sections() + """configSections = shared.config.addresses() for addressInKeysFile in configSections: if addressInKeysFile <> 'bitmessagesettings': status,addressVersionNumber,streamNumber,hashFromThisParticularAddress = decodeAddress(addressInKeysFile) diff --git a/src/class_smtpDeliver.py b/src/class_smtpDeliver.py index 9492f123..bb659ebe 100644 --- a/src/class_smtpDeliver.py +++ b/src/class_smtpDeliver.py @@ -59,7 +59,7 @@ class smtpDeliver(threading.Thread, StoppableThread): msg = MIMEText(body, 'plain', 'utf-8') msg['Subject'] = Header(subject, 'utf-8') msg['From'] = fromAddress + '@' + SMTPDOMAIN - toLabel = map (lambda y: BMConfigParser().safeGet(y, "label"), filter(lambda x: x == toAddress, BMConfigParser().sections())) + toLabel = map (lambda y: BMConfigParser().safeGet(y, "label"), filter(lambda x: x == toAddress, BMConfigParser().addresses())) if len(toLabel) > 0: msg['To'] = "\"%s\" <%s>" % (Header(toLabel[0], 'utf-8'), toAddress + '@' + SMTPDOMAIN) else: diff --git a/src/class_smtpServer.py b/src/class_smtpServer.py index 13882a94..3bc81a61 100644 --- a/src/class_smtpServer.py +++ b/src/class_smtpServer.py @@ -115,7 +115,7 @@ class smtpServerPyBitmessage(smtpd.SMTPServer): sender, domain = p.sub(r'\1', mailfrom).split("@") if domain != SMTPDOMAIN: raise Exception("Bad domain %s", domain) - if sender not in BMConfigParser().sections(): + if sender not in BMConfigParser().addresses(): raise Exception("Nonexisting user %s", sender) except Exception as err: logger.debug("Bad envelope from %s: %s", mailfrom, repr(err)) @@ -125,7 +125,7 @@ class smtpServerPyBitmessage(smtpd.SMTPServer): sender, domain = msg_from.split("@") if domain != SMTPDOMAIN: raise Exception("Bad domain %s", domain) - if sender not in BMConfigParser().sections(): + if sender not in BMConfigParser().addresses(): raise Exception("Nonexisting user %s", sender) except Exception as err: logger.error("Bad headers from %s: %s", msg_from, repr(err)) diff --git a/src/class_sqlThread.py b/src/class_sqlThread.py index 9bd7e8e7..18606e74 100644 --- a/src/class_sqlThread.py +++ b/src/class_sqlThread.py @@ -316,7 +316,7 @@ class sqlThread(threading.Thread): # Adjust the required POW values for each of this user's addresses to conform to protocol v3 norms. if BMConfigParser().getint('bitmessagesettings', 'settingsversion') == 9: - for addressInKeysFile in BMConfigParser().sections(): + for addressInKeysFile in BMConfigParser().addressses(): try: previousTotalDifficulty = float(BMConfigParser().getint(addressInKeysFile, 'noncetrialsperbyte')) / 320 previousSmallMessageDifficulty = float(BMConfigParser().getint(addressInKeysFile, 'payloadlengthextrabytes')) / 14000 diff --git a/src/shared.py b/src/shared.py index 4fd66610..34597c78 100644 --- a/src/shared.py +++ b/src/shared.py @@ -110,29 +110,27 @@ def reloadMyAddressHashes(): #myPrivateKeys.clear() keyfileSecure = checkSensitiveFilePermissions(state.appdata + 'keys.dat') - configSections = BMConfigParser().sections() hasEnabledKeys = False - for addressInKeysFile in configSections: - if addressInKeysFile <> 'bitmessagesettings': - isEnabled = BMConfigParser().getboolean(addressInKeysFile, 'enabled') - if isEnabled: - hasEnabledKeys = True - status,addressVersionNumber,streamNumber,hash = decodeAddress(addressInKeysFile) - if addressVersionNumber == 2 or addressVersionNumber == 3 or addressVersionNumber == 4: - # Returns a simple 32 bytes of information encoded in 64 Hex characters, - # or null if there was an error. - privEncryptionKey = hexlify(decodeWalletImportFormat( - BMConfigParser().get(addressInKeysFile, 'privencryptionkey'))) + for addressInKeysFile in BMConfigParser().addresses(): + isEnabled = BMConfigParser().getboolean(addressInKeysFile, 'enabled') + if isEnabled: + hasEnabledKeys = True + status,addressVersionNumber,streamNumber,hash = decodeAddress(addressInKeysFile) + if addressVersionNumber == 2 or addressVersionNumber == 3 or addressVersionNumber == 4: + # Returns a simple 32 bytes of information encoded in 64 Hex characters, + # or null if there was an error. + privEncryptionKey = hexlify(decodeWalletImportFormat( + BMConfigParser().get(addressInKeysFile, 'privencryptionkey'))) - if len(privEncryptionKey) == 64:#It is 32 bytes encoded as 64 hex characters - myECCryptorObjects[hash] = highlevelcrypto.makeCryptor(privEncryptionKey) - myAddressesByHash[hash] = addressInKeysFile - tag = hashlib.sha512(hashlib.sha512(encodeVarint( - addressVersionNumber) + encodeVarint(streamNumber) + hash).digest()).digest()[32:] - myAddressesByTag[tag] = addressInKeysFile + if len(privEncryptionKey) == 64:#It is 32 bytes encoded as 64 hex characters + myECCryptorObjects[hash] = highlevelcrypto.makeCryptor(privEncryptionKey) + myAddressesByHash[hash] = addressInKeysFile + tag = hashlib.sha512(hashlib.sha512(encodeVarint( + addressVersionNumber) + encodeVarint(streamNumber) + hash).digest()).digest()[32:] + myAddressesByTag[tag] = addressInKeysFile - else: - logger.error('Error in reloadMyAddressHashes: Can\'t handle address versions other than 2, 3, or 4.\n') + else: + logger.error('Error in reloadMyAddressHashes: Can\'t handle address versions other than 2, 3, or 4.\n') if not keyfileSecure: fixSensitiveFilePermissions(state.appdata + 'keys.dat', hasEnabledKeys) From 183f509f094020f3a30810a99108623e16863c6d Mon Sep 17 00:00:00 2001 From: Peter Surda Date: Mon, 15 May 2017 12:23:16 +0200 Subject: [PATCH 045/407] Decompression limit - there is now a configurable decompression limit, default at 1MB. Oversize messages are trated as if they never arrived, just a log entry --- src/class_objectProcessor.py | 3 ++- src/helper_msgcoding.py | 26 ++++++++++++++++++++++---- 2 files changed, 24 insertions(+), 5 deletions(-) diff --git a/src/class_objectProcessor.py b/src/class_objectProcessor.py index ad78bd87..253e6808 100644 --- a/src/class_objectProcessor.py +++ b/src/class_objectProcessor.py @@ -28,7 +28,6 @@ import tr from debug import logger import l10n - class objectProcessor(threading.Thread): """ The objectProcessor thread, of which there is only one, receives network @@ -71,6 +70,8 @@ class objectProcessor(threading.Thread): pass else: logger.critical('Error! Bug! The class_objectProcessor was passed an object type it doesn\'t recognize: %s' % str(objectType)) + except helper_msgcoding.DecompressionSizeException as e: + logger.error("The object is too big after decompression (stopped decompressing at %ib, your configured limit %ib). Ignoring", e.size, BMConfigParser().safeGetInt("zlib", "maxsize")) except varintDecodeError as e: logger.debug("There was a problem with a varint while processing an object. Some details: %s" % e) except Exception as e: diff --git a/src/helper_msgcoding.py b/src/helper_msgcoding.py index 2acdd6a4..a507685e 100644 --- a/src/helper_msgcoding.py +++ b/src/helper_msgcoding.py @@ -7,6 +7,7 @@ except ImportError: import string import zlib +from bmconfigparser import BMConfigParser import shared from debug import logger import messagetypes @@ -17,6 +18,10 @@ BITMESSAGE_ENCODING_TRIVIAL = 1 BITMESSAGE_ENCODING_SIMPLE = 2 BITMESSAGE_ENCODING_EXTENDED = 3 +class DecompressionSizeException(Exception): + def __init__(self, size): + self.size = size + class MsgEncode(object): def __init__(self, message, encoding=BITMESSAGE_ENCODING_SIMPLE): @@ -65,11 +70,24 @@ class MsgDecode(object): self.subject = _translate("MsgDecode", "Unknown encoding") def decodeExtended(self, data): + dc = zlib.decompressobj() + tmp = "" + while len(tmp) <= BMConfigParser().safeGetInt("zlib", "maxsize"): + try: + got = dc.decompress(data, BMConfigParser().safeGetInt("zlib", "maxsize") + 1 - len(tmp)) + # EOF + if got == "": + break + tmp += got + data = dc.unconsumed_tail + except zlib.error: + logger.error("Error decompressing message") + raise + else: + raise DecompressionSizeException(len(tmp)) + try: - tmp = msgpack.loads(zlib.decompress(data)) - except zlib.error: - logger.error("Error decompressing message") - raise + tmp = msgpack.loads(tmp) except (msgpack.exceptions.UnpackException, msgpack.exceptions.ExtraData): logger.error("Error msgunpacking message") From 198470f734aa6cc8744d40af50fea5dc5ac3c815 Mon Sep 17 00:00:00 2001 From: Peter Surda Date: Mon, 15 May 2017 12:25:30 +0200 Subject: [PATCH 046/407] Revert parser/renderer max message size - it's now dealt with during decoding --- src/bitmessageqt/messageview.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/bitmessageqt/messageview.py b/src/bitmessageqt/messageview.py index 882dbb81..40830a70 100644 --- a/src/bitmessageqt/messageview.py +++ b/src/bitmessageqt/messageview.py @@ -128,7 +128,6 @@ class MessageView(QtGui.QTextBrowser): self.html.reset() self.html.reset_safe() self.html.allow_picture = True - # quick hack to limit excessively compressed messages, limits viewing to first MB of data - self.html.feed(data[0:1048576]) + self.html.feed(data) self.html.close() self.showPlain() From d498f1c0aebe73394080cc0b254ee0eebf8bd058 Mon Sep 17 00:00:00 2001 From: Peter Surda Date: Wed, 24 May 2017 16:49:16 +0200 Subject: [PATCH 047/407] Configparser update - add default values for maxdownload/uploadrate, asyncore - rework error handler slightly --- src/bmconfigparser.py | 25 +++++++++++++++---------- 1 file changed, 15 insertions(+), 10 deletions(-) diff --git a/src/bmconfigparser.py b/src/bmconfigparser.py index 448a9ffc..4e66c703 100644 --- a/src/bmconfigparser.py +++ b/src/bmconfigparser.py @@ -10,8 +10,13 @@ BMConfigDefaults = { "bitmessagesettings": { "maxaddrperstreamsend": 500, "maxbootstrapconnections": 20, + "maxdownloadrate": 0, "maxoutboundconnections": 8, "maxtotalconnections": 200, + "maxuploadrate": 0, + }, + "network": { + "asyncore": False }, "zlib": { 'maxsize': 1048576 @@ -27,35 +32,35 @@ class BMConfigParser(ConfigParser.SafeConfigParser): return ConfigParser.ConfigParser.set(self, section, option, value) def get(self, section, option, raw=False, vars=None): - if section == "bitmessagesettings" and option == "timeformat": - try: - return ConfigParser.ConfigParser.get(self, section, option, raw, vars) - except ConfigParser.InterpolationError: - return ConfigParser.ConfigParser.get(self, section, option, True, vars) try: - return ConfigParser.ConfigParser.get(self, section, option, True, vars) + if section == "bitmessagesettings" and option == "timeformat": + return ConfigParser.ConfigParser.get(self, section, option, raw, vars) + else: + return ConfigParser.ConfigParser.get(self, section, option, True, vars) + except ConfigParser.InterpolationError: + return ConfigParser.ConfigParser.get(self, section, option, True, vars) except (ConfigParser.NoSectionError, ConfigParser.NoOptionError) as e: try: return BMConfigDefaults[section][option] - except KeyError: + except (KeyError, ValueError, AttributeError): raise e def safeGetBoolean(self, section, field): try: return self.getboolean(section, field) - except (ConfigParser.NoSectionError, ConfigParser.NoOptionError, ValueError): + except (ConfigParser.NoSectionError, ConfigParser.NoOptionError, ValueError, AttributeError): return False def safeGetInt(self, section, field, default=0): try: return self.getint(section, field) - except (ConfigParser.NoSectionError, ConfigParser.NoOptionError, ValueError): + except (ConfigParser.NoSectionError, ConfigParser.NoOptionError, ValueError, AttributeError): return default def safeGet(self, section, option, default = None): try: return self.get(section, option) - except (ConfigParser.NoSectionError, ConfigParser.NoOptionError, ValueError): + except (ConfigParser.NoSectionError, ConfigParser.NoOptionError, ValueError, AttributeError): return default def items(self, section, raw=False, vars=None): From d635e515b965141e710a7d93137aa1942b2dc204 Mon Sep 17 00:00:00 2001 From: Peter Surda Date: Wed, 24 May 2017 16:51:49 +0200 Subject: [PATCH 048/407] Big Asyncore update - most of the stuff is done so it partially works - disabled pollers other than select (debugging necessary) - can switch in the settings, section network, option asyncore (defaults to False) --- src/bitmessagemain.py | 30 ++- src/network/advanceddispatcher.py | 29 ++- src/network/asyncore_pollchoose.py | 95 ++++++- src/network/bmobject.py | 94 +++++++ src/{ => network}/bmproto.py | 391 ++++++++++++++++++++++------- src/network/bmqueues.py | 95 +++++++ src/network/connectionchooser.py | 11 + src/network/connectionpool.py | 149 +++++++++++ src/network/downloadqueue.py | 12 + src/network/networkthread.py | 40 +++ src/network/node.py | 67 +---- src/network/tls.py | 29 +-- src/network/uploadqueue.py | 70 ++++++ src/protocol.py | 6 + 14 files changed, 928 insertions(+), 190 deletions(-) create mode 100644 src/network/bmobject.py rename src/{ => network}/bmproto.py (51%) create mode 100644 src/network/bmqueues.py create mode 100644 src/network/connectionchooser.py create mode 100644 src/network/connectionpool.py create mode 100644 src/network/downloadqueue.py create mode 100644 src/network/networkthread.py create mode 100644 src/network/uploadqueue.py diff --git a/src/bitmessagemain.py b/src/bitmessagemain.py index c98f7592..e61675cf 100755 --- a/src/bitmessagemain.py +++ b/src/bitmessagemain.py @@ -50,6 +50,9 @@ from class_smtpDeliver import smtpDeliver from class_smtpServer import smtpServer from bmconfigparser import BMConfigParser +from network.connectionpool import BMConnectionPool +from network.networkthread import BMNetworkThread + # Helper Functions import helper_bootstrap import helper_generic @@ -80,10 +83,13 @@ def connectToStream(streamNumber): if streamNumber*2+1 not in knownnodes.knownNodes: knownnodes.knownNodes[streamNumber*2+1] = {} - for i in range(maximumNumberOfHalfOpenConnections): - a = outgoingSynSender() - a.setup(streamNumber, selfInitiatedConnections) - a.start() + if BMConfigParser().safeGetBoolean("network", "asyncore"): + BMConnectionPool().connectToStream(streamNumber) + else: + for i in range(maximumNumberOfHalfOpenConnections): + a = outgoingSynSender() + a.setup(streamNumber, selfInitiatedConnections) + a.start() def _fixWinsock(): if not ('win32' in sys.platform) and not ('win64' in sys.platform): @@ -242,13 +248,19 @@ class Main: singleAPIThread.daemon = True # close the main program even if there are threads left singleAPIThread.start() + if BMConfigParser().safeGetBoolean("network", "asyncore"): + asyncoreThread = BMNetworkThread() + asyncoreThread.daemon = False + asyncoreThread.start() + connectToStream(1) - singleListenerThread = singleListener() - singleListenerThread.setup(selfInitiatedConnections) - singleListenerThread.daemon = True # close the main program even if there are threads left - singleListenerThread.start() - + if not BMConfigParser().safeGetBoolean("network", "asyncore"): + singleListenerThread = singleListener() + singleListenerThread.setup(selfInitiatedConnections) + singleListenerThread.daemon = True # close the main program even if there are threads left + singleListenerThread.start() + if BMConfigParser().safeGetBoolean('bitmessagesettings','upnp'): import upnp upnpThread = upnp.uPnPThread() diff --git a/src/network/advanceddispatcher.py b/src/network/advanceddispatcher.py index dc7eedb0..f4ba120e 100644 --- a/src/network/advanceddispatcher.py +++ b/src/network/advanceddispatcher.py @@ -1,4 +1,7 @@ +import time + import asyncore_pollchoose as asyncore +from bmconfigparser import BMConfigParser class AdvancedDispatcher(asyncore.dispatcher): _buf_len = 2097152 # 2MB @@ -9,6 +12,7 @@ class AdvancedDispatcher(asyncore.dispatcher): self.read_buf = b"" self.write_buf = b"" self.state = "init" + self.lastTx = time.time() def append_write_buf(self, string = None): self.write_buf += string @@ -32,7 +36,7 @@ class AdvancedDispatcher(asyncore.dispatcher): return while True: try: - print "Trying to handle state \"%s\"" % (self.state) +# print "Trying to handle state \"%s\"" % (self.state) if getattr(self, "state_" + str(self.state))() is False: break except AttributeError: @@ -50,13 +54,30 @@ class AdvancedDispatcher(asyncore.dispatcher): return self.connecting or len(self.read_buf) < AdvancedDispatcher._buf_len def handle_read(self): - print "handle_read" - self.read_buf += self.recv(AdvancedDispatcher._buf_len) + self.lastTx = time.time() + if asyncore.maxDownloadRate > 0: + newData = self.recv(asyncore.downloadChunk) + asyncore.downloadBucket -= len(newData) + self.read_buf += newData + else: + self.read_buf += self.recv(AdvancedDispatcher._buf_len) self.process() def handle_write(self): - written = self.send(self.write_buf) + self.lastTx = time.time() + if asyncore.maxUploadRate > 0: + written = self.send(self.write_buf[0:asyncore.uploadChunk]) + asyncore.uploadBucket -= written + else: + written = self.send(self.write_buf) self.slice_write_buf(written) def handle_connect(self): + self.lastTx = time.time() self.process() + + def close(self): + self.read_buf = b"" + self.write_buf = b"" + self.state = "shutdown" + asyncore.dispatcher.close(self) diff --git a/src/network/asyncore_pollchoose.py b/src/network/asyncore_pollchoose.py index 4ccce7f9..b26d4cab 100644 --- a/src/network/asyncore_pollchoose.py +++ b/src/network/asyncore_pollchoose.py @@ -46,6 +46,8 @@ many of the difficult problems for you, making the task of building sophisticated high-performance network servers and clients a snap. """ +# randomise object order for bandwidth balancing +import random import select import socket import sys @@ -56,6 +58,11 @@ import os from errno import EALREADY, EINPROGRESS, EWOULDBLOCK, ECONNRESET, EINVAL, \ ENOTCONN, ESHUTDOWN, EISCONN, EBADF, ECONNABORTED, EPIPE, EAGAIN, \ errorcode +try: + from errno import WSAEWOULDBLOCK +except: + pass +from ssl import SSLError, SSL_ERROR_WANT_READ, SSL_ERROR_WANT_WRITE _DISCONNECTED = frozenset((ECONNRESET, ENOTCONN, ESHUTDOWN, ECONNABORTED, EPIPE, EBADF)) @@ -81,6 +88,15 @@ class ExitNow(Exception): _reraised_exceptions = (ExitNow, KeyboardInterrupt, SystemExit) +maxDownloadRate = 0 +downloadChunk = 0 +downloadTimestamp = 0 +downloadBucket = 0 +maxUploadRate = 0 +uploadChunk = 0 +uploadTimestamp = 0 +uploadBucket = 0 + def read(obj): try: obj.handle_read_event() @@ -97,6 +113,44 @@ def write(obj): except: obj.handle_error() +def set_rates(download, upload): + global maxDownloadRate, maxUploadRate, downloadChunk, uploadChunk, downloadBucket, uploadBucket, downloadTimestamp, uploadTimestamp + maxDownloadRate = float(download) + if maxDownloadRate > 0: + downloadChunk = 1400 + maxUploadRate = float(upload) + if maxUploadRate > 0: + uploadChunk = 1400 + downloadBucket = maxDownloadRate + uploadBucket = maxUploadRate + downloadTimestamp = time.time() + uploadTimestamp = time.time() + +def wait_tx_buckets(): + global downloadBucket, uploadBucket, downloadTimestamp, uploadTimestamp + if maxDownloadRate > 0 and maxUploadRate > 0: + wait_for_this_long = min(maxDownloadRate / downloadChunk, maxUploadRate / uploadChunk) + elif maxDownloadRate > 0: + wait_for_this_long = maxDownloadRate / downloadChunk + elif maxUploadRate > 0: + wait_for_this_long = maxUploadRate / uploadChunk + else: + return + wait_for_this_long /= 2 + if wait_for_this_long > 1: + wait_for_this_long = 1 + elif wait_for_this_long < 0.1: + wait_for_this_long = 0.1 + + while downloadBucket < downloadChunk and uploadBucket < uploadChunk: + time.sleep(wait_for_this_long) + downloadBucket += (time.time() - downloadTimestamp) * maxDownloadRate + downloadTimestamp = time.time() + uploadBucket += (time.time() - uploadTimestamp) * maxUploadRate + uploadTimestamp = time.time() + + + def _exception(obj): try: obj.handle_expt_event() @@ -150,13 +204,13 @@ def select_poller(timeout=0.0, map=None): except KeyboardInterrupt: return - for fd in r: + for fd in random.sample(r, len(r)): obj = map.get(fd) if obj is None: continue read(obj) - for fd in w: + for fd in random.sample(w, len(w)): obj = map.get(fd) if obj is None: continue @@ -204,7 +258,7 @@ def poll_poller(timeout=0.0, map=None): r = poll_poller.pollster.poll(timeout) except KeyboardInterrupt: r = [] - for fd, flags in r: + for fd, flags in random.sample(r, len(r)): obj = map.get(fd) if obj is None: continue @@ -252,7 +306,7 @@ def epoll_poller(timeout=0.0, map=None): if err.args[0] != EINTR: raise r = [] - for fd, flags in r: + for fd, flags in random.sample(r, len(r)): obj = map.get(fd) if obj is None: continue @@ -278,7 +332,7 @@ def kqueue_poller(timeout=0.0, map=None): selectables += 1 events = kqueue.control(None, selectables, timeout) - for event in events: + for event in random.sample(events, len(events)): fd = event.ident obj = map.get(fd) if obj is None: @@ -307,13 +361,18 @@ def loop(timeout=30.0, use_poll=False, map=None, count=None, elif hasattr(select, 'select'): poller = select_poller - print "Poll loop using %s" % (poller.__name__) + poller = select_poller + +# print "Poll loop using %s" % (poller.__name__) if count is None: while map: + wait_tx_buckets() poller(timeout, map) else: + timeout /= count while map and count > 0: + wait_tx_buckets() poller(timeout, map) count = count - 1 @@ -482,10 +541,17 @@ class dispatcher: try: result = self.socket.send(data) return result - except socket.error as why: - if why.args[0] == EWOULDBLOCK: + except SSLError as err: + if err.errno == SSL_ERROR_WANT_WRITE: return 0 - elif why.args[0] in _DISCONNECTED: + else: + raise + except socket.error as why: + if why.errno in (errno.EAGAIN, errno.EWOULDBLOCK) or \ + (sys.platform.startswith('win') and \ + err.errno == errno.WSAEWOULDBLOCK): + return 0 + elif why.errno in _DISCONNECTED: self.handle_close() return 0 else: @@ -501,9 +567,18 @@ class dispatcher: return b'' else: return data + except SSLError as err: + if err.errno == SSL_ERROR_WANT_READ: + return b'' + else: + raise except socket.error as why: # winsock sometimes raises ENOTCONN - if why.args[0] in _DISCONNECTED: + if why.errno in (errno.EAGAIN, errno.EWOULDBLOCK) or \ + (sys.platform.startswith('win') and \ + err.errno == errno.WSAEWOULDBLOCK): + return b'' + if why.errno in _DISCONNECTED: self.handle_close() return b'' else: diff --git a/src/network/bmobject.py b/src/network/bmobject.py new file mode 100644 index 00000000..2c3fb59c --- /dev/null +++ b/src/network/bmobject.py @@ -0,0 +1,94 @@ +from binascii import hexlify +import time + +from addresses import calculateInventoryHash +from debug import logger +import protocol +import state + +class BMObjectInsufficientPOWError(Exception): pass + + +class BMObjectInvalidDataError(Exception): pass + + +class BMObjectExpiredError(Exception): pass + + +class BMObjectUnwantedStreamError(Exception): pass + + +class BMObjectInvalidError(Exception): pass + + +class BMObjectAlreadyHaveError(Exception): + pass + + +class BMObject(object): + # max TTL, 28 days and 3 hours + maxTTL = 28 * 24 * 60 * 60 + 10800 + # min TTL, 3 hour (in the past + minTTL = -3600 + + def __init__(self, nonce, expiresTime, objectType, version, streamNumber, data): + self.nonce = nonce + self.expiresTime = expiresTime + self.objectType = objectType + self.version = version + self.streamNumber = streamNumber + self.inventoryHash = calculateInventoryHash(data) + self.data = data + self.tag = '' + + def checkProofOfWorkSufficient(self): + # Let us check to make sure that the proof of work is sufficient. + if not protocol.isProofOfWorkSufficient(self.data): + logger.info('Proof of work is insufficient.') + raise BMObjectInsufficientPOWError() + + def checkEOLSanity(self): + # EOL sanity check + if self.expiresTime - int(time.time()) > BMObject.maxTTL: + logger.info('This object\'s End of Life time is too far in the future. Ignoring it. Time is %s' % self.expiresTime) + # TODO: remove from download queue + raise BMObjectExpiredError() + + if self.expiresTime - int(time.time()) < BMObject.minTTL: + logger.info('This object\'s End of Life time was too long ago. Ignoring the object. Time is %s' % self.expiresTime) + # TODO: remove from download queue + raise BMObjectExpiredError() + + def checkStream(self): + if self.streamNumber not in state.streamsInWhichIAmParticipating: + logger.debug('The streamNumber %s isn\'t one we are interested in.' % self.streamNumber) + raise BMObjectUnwantedStreamError() + + def checkMessage(self): + return + + def checkGetpubkey(self): + if len(self.data) < 42: + logger.info('getpubkey message doesn\'t contain enough data. Ignoring.') + raise BMObjectInvalidError() + + def checkPubkey(self, tag): + if len(self.data) < 146 or len(self.data) > 440: # sanity check + logger.info('pubkey object too short or too long. Ignoring.') + raise BMObjectInvalidError() + if self.version >= 4: + self.tag = tag + logger.debug('tag in received pubkey is: %s' % hexlify(tag)) + + def checkBroadcast(self, tag): + if len(self.data) < 180: + logger.debug('The payload length of this broadcast packet is unreasonably low. Someone is probably trying funny business. Ignoring message.') + raise BMObjectInvalidError() + + # this isn't supported anymore + if self.version < 2: + raise BMObjectInvalidError() + + if self.version >= 3: + self.tag = tag + logger.debug('tag in received broadcast is: %s' % hexlify(tag)) diff --git a/src/bmproto.py b/src/network/bmproto.py similarity index 51% rename from src/bmproto.py rename to src/network/bmproto.py index c9160c3b..d9ed2a95 100644 --- a/src/bmproto.py +++ b/src/network/bmproto.py @@ -1,28 +1,52 @@ +import base64 +from binascii import hexlify import hashlib +import math import time from pprint import pprint import socket -from struct import unpack +import struct +import random +import traceback +from addresses import calculateInventoryHash +from debug import logger +from inventory import Inventory +import knownnodes from network.advanceddispatcher import AdvancedDispatcher +from network.bmobject import BMObject, BMObjectInsufficientPOWError, BMObjectInvalidDataError, BMObjectExpiredError, BMObjectUnwantedStreamError, BMObjectInvalidError, BMObjectAlreadyHaveError +import network.connectionpool +from network.downloadqueue import DownloadQueue from network.node import Node import network.asyncore_pollchoose as asyncore from network.proxy import Proxy, ProxyError, GeneralProxyError +from network.bmqueues import BMQueues from network.socks5 import Socks5Connection, Socks5Resolver, Socks5AuthError, Socks5Error from network.socks4a import Socks4aConnection, Socks4aResolver, Socks4aError +from network.uploadqueue import UploadQueue, UploadElem, AddrUploadQueue, ObjUploadQueue from network.tls import TLSDispatcher import addresses from bmconfigparser import BMConfigParser +from queues import objectProcessorQueue import shared +import state import protocol class BMProtoError(ProxyError): pass -class BMConnection(TLSDispatcher): +class BMProtoInsufficientDataError(BMProtoError): pass + + +class BMProtoExcessiveDataError(BMProtoError): pass + + +class BMConnection(TLSDispatcher, BMQueues): # ~1.6 MB which is the maximum possible size of an inv message. maxMessageSize = 1600100 + # 2**18 = 256kB is the maximum size of an object payload + maxObjectPayloadSize = 2**18 # protocol specification says max 1000 addresses in one addr command maxAddrCount = 1000 # protocol specification says max 50000 objects in one inv command @@ -32,46 +56,74 @@ class BMConnection(TLSDispatcher): AdvancedDispatcher.__init__(self, sock) self.verackReceived = False self.verackSent = False + self.lastTx = time.time() + self.connectionFullyEstablished = False + self.connectedAt = 0 + self.skipUntil = 0 if address is None and sock is not None: - self.destination = self.addr() + self.destination = state.Peer(sock.getpeername()[0], sock.getpeername()[1]) self.isOutbound = False TLSDispatcher.__init__(self, sock, server_side=True) - print "received connection in background from %s:%i" % (self.destination[0], self.destination[1]) + self.connectedAt = time.time() + print "received connection in background from %s:%i" % (self.destination.host, self.destination.port) else: self.destination = address self.isOutbound = True - self.create_socket(socket.AF_INET, socket.SOCK_STREAM) - self.connect(self.destination) + if ":" in address.host: + self.create_socket(socket.AF_INET6, socket.SOCK_STREAM) + else: + self.create_socket(socket.AF_INET, socket.SOCK_STREAM) + self.socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) TLSDispatcher.__init__(self, sock, server_side=False) - print "connecting in background to %s:%i" % (self.destination[0], self.destination[1]) + self.connect(self.destination) + print "connecting in background to %s:%i" % (self.destination.host, self.destination.port) + shared.connectedHostsList[self.destination] = 0 + BMQueues.__init__(self) def bm_proto_reset(self): self.magic = None self.command = None - self.payloadLength = None + self.payloadLength = 0 self.checksum = None self.payload = None self.invalid = False self.payloadOffset = 0 + self.object = None def state_init(self): self.bm_proto_reset() - self.append_write_buf(protocol.assembleVersionMessage(self.destination[0], self.destination[1], (1,), False)) - if True: - print "Sending version (%ib)" % len(self.write_buf) - self.set_state("bm_header") - return False + if self.isOutbound: + self.append_write_buf(protocol.assembleVersionMessage(self.destination.host, self.destination.port, network.connectionpool.BMConnectionPool().streams, False)) + print "%s:%i: Sending version (%ib)" % (self.destination.host, self.destination.port, len(self.write_buf)) + self.set_state("bm_header") + return True - def state_bm_ready(self): - print "doing bm ready" + def antiIntersectionDelay(self, initial = False): + # estimated time for a small object to propagate across the whole network + delay = math.ceil(math.log(max(len(knownnodes.knownNodes[x]) for x in knownnodes.knownNodes) + 2, 20)) * (0.2 + UploadQueue.queueCount/2) + # take the stream with maximum amount of nodes + # +2 is to avoid problems with log(0) and log(1) + # 20 is avg connected nodes count + # 0.2 is avg message transmission time + if delay > 0: + if initial: + self.skipUntil = self.connectedAt + delay + if self.skipUntil > time.time(): + logger.debug("Skipping processing for %.2fs", self.skipUntil - time.time()) + else: + logger.debug("Skipping processing due to missing object for %.2fs", self.skipUntil - time.time()) + self.skipUntil = time.time() + now + + def set_connection_fully_established(self): + self.antiIntersectionDelay(True) + self.connectionFullyEstablished = True self.sendAddr() self.sendBigInv() - self.set_state("bm_header") - return False def state_bm_header(self): + #print "%s:%i: header" % (self.destination.host, self.destination.port) if len(self.read_buf) < protocol.Header.size: - print "Length below header size" + #print "Length below header size" return False self.magic, self.command, self.payloadLength, self.checksum = protocol.Header.unpack(self.read_buf[:protocol.Header.size]) self.command = self.command.rstrip('\x00') @@ -87,20 +139,41 @@ class BMConnection(TLSDispatcher): def state_bm_command(self): if len(self.read_buf) < self.payloadLength: - print "Length below announced object length" + #print "Length below announced object length" return False - print "received %s (%ib)" % (self.command, self.payloadLength) + print "%s:%i: command %s (%ib)" % (self.destination.host, self.destination.port, self.command, self.payloadLength) self.payload = self.read_buf[:self.payloadLength] if self.checksum != hashlib.sha512(self.payload).digest()[0:4]: print "Bad checksum, ignoring" self.invalid = True retval = True + if not self.connectionFullyEstablished and self.command not in ("version", "verack"): + logger.error("Received command %s before connection was fully established, ignoring", self.command) + self.invalid = True if not self.invalid: try: retval = getattr(self, "bm_command_" + str(self.command).lower())() except AttributeError: # unimplemented command print "unimplemented command %s" % (self.command) + except BMProtoInsufficientDataError: + print "packet length too short, skipping" + except BMProtoExcessiveDataError: + print "too much data, skipping" + except BMObjectInsufficientPOWError: + print "insufficient PoW, skipping" + except BMObjectInvalidDataError: + print "object invalid data, skipping" + except BMObjectExpiredError: + print "object expired, skipping" + except BMObjectUnwantedStreamError: + print "object not in wanted stream, skipping" + except BMObjectInvalidError: + print "object invalid, skipping" + except BMObjectAlreadyHaveError: + print "already got object, skipping" + except struct.error: + print "decoding error, skipping" else: print "Skipping command %s due to invalid data" % (self.command) if retval: @@ -120,11 +193,24 @@ class BMConnection(TLSDispatcher): return value def decode_payload_node(self): - services, address, port = self.decode_payload_content("Q16sH") - return Node(services, address, port) + services, host, port = self.decode_payload_content("Q16sH") + if host[0:12] == '\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xFF\xFF': + host = socket.inet_ntop(socket.AF_INET, host[12:]) + elif host[0:6] == '\xfd\x87\xd8\x7e\xeb\x43': + # Onion, based on BMD/bitcoind + host = base64.b32encode(host[6:]).lower() + ".onion" + else: + host = socket.inet_ntop(socket.AF_INET6, host) + if host == "": + # This can happen on Windows systems which are not 64-bit compatible + # so let us drop the IPv6 address. + host = socket.inet_ntop(socket.AF_INET, host[12:]) + + return Node(services, host, port) def decode_payload_content(self, pattern = "v"): - # l = varint indicating the length of the next item + # l = varint indicating the length of the next array + # L = varint indicating the length of the next item # v = varint (or array) # H = uint16 # I = uint32 @@ -135,86 +221,171 @@ class BMConnection(TLSDispatcher): # , = end of array retval = [] - size = 0 + size = None insideDigit = False + i = 0 - for i in range(len(pattern)): - if pattern[i] in "0123456789": + while i < len(pattern): + if pattern[i] in "0123456789" and (i == 0 or pattern[i-1] not in "lL"): + if size is None: + size = 0 size = size * 10 + int(pattern[i]) + i += 1 continue - elif pattern[i] == "l": + elif pattern[i] == "l" and size is None: size = self.decode_payload_varint() + i += 1 continue - if size > 0: - innerval = [] + elif pattern[i] == "L" and size is None: + size = self.decode_payload_varint() + i += 1 + continue + if size is not None: if pattern[i] == "s": retval.append(self.payload[self.payloadOffset:self.payloadOffset + size]) self.payloadOffset += size + i += 1 else: + if "," in pattern[i:]: + subpattern = pattern[i:pattern.index(",")] + else: + subpattern = pattern[i:] + for j in range(size): - if "," in pattern[i:]: - retval.append(self.decode_payload_content(pattern[i:pattern.index(",")])) + if pattern[i-1:i] == "L": + retval.extend(self.decode_payload_content(subpattern)) else: - retval.append(self.decode_payload_content(pattern[i:])) - size = 0 + retval.append(self.decode_payload_content(subpattern)) + i += len(subpattern) + size = None else: if pattern[i] == "v": retval.append(self.decode_payload_varint()) if pattern[i] == "i": retval.append(self.decode_payload_node()) if pattern[i] == "H": - retval.append(unpack(">H", self.payload[self.payloadOffset:self.payloadOffset+2])[0]) + retval.append(struct.unpack(">H", self.payload[self.payloadOffset:self.payloadOffset+2])[0]) self.payloadOffset += 2 if pattern[i] == "I": - retval.append(unpack(">I", self.payload[self.payloadOffset:self.payloadOffset+4])[0]) + retval.append(struct.unpack(">I", self.payload[self.payloadOffset:self.payloadOffset+4])[0]) self.payloadOffset += 4 if pattern[i] == "Q": - retval.append(unpack(">Q", self.payload[self.payloadOffset:self.payloadOffset+8])[0]) + retval.append(struct.unpack(">Q", self.payload[self.payloadOffset:self.payloadOffset+8])[0]) self.payloadOffset += 8 + i += 1 + if self.payloadOffset > self.payloadLength: + print "Insufficient data %i/%i" % (self.payloadOffset, self.payloadLength) + raise BMProtoInsufficientDataError() return retval def bm_command_error(self): fatalStatus, banTime, inventoryVector, errorText = self.decode_payload_content("vvlsls") + print "%s:%i error: %i, %s" % (self.destination.host, self.destination.port, fatalStatus, errorText) + return True def bm_command_getdata(self): - items = self.decode_payload_content("l32s") - #self.antiIntersectionDelay(True) # only handle getdata requests if we have been connected long enough + items = self.decode_payload_content("L32s") +# if time.time() < self.skipUntil: +# print "skipping getdata" +# return True for i in items: - logger.debug('received getdata request for item:' + hexlify(i)) - if self.objectHashHolderInstance.hasHash(i): + print "received getdata request for item %s" % (hexlify(i)) + #logger.debug('received getdata request for item:' + hexlify(i)) + #if i in ObjUploadQueue.streamElems(1): + if False: self.antiIntersectionDelay() else: if i in Inventory(): self.append_write_buf(protocol.CreatePacket('object', Inventory()[i].payload)) else: - #self.antiIntersectionDelay() + self.antiIntersectionDelay() logger.warning('%s asked for an object with a getdata which is not in either our memory inventory or our SQL inventory. We probably cleaned it out after advertising it but before they got around to asking for it.' % (self.peer,)) + return True + + def bm_command_inv(self): + items = self.decode_payload_content("L32s") + + if len(items) >= BMConnection.maxObjectCount: + logger.error("Too many items in inv message!") + raise BMProtoExcessiveDataError() + else: + print "items in inv: %i" % (len(items)) + + startTime = time.time() + #advertisedSet = set() + for i in items: + #advertisedSet.add(i) + self.handleReceivedObj(i) + #objectsNewToMe = advertisedSet + #for stream in self.streams: + #objectsNewToMe -= Inventory().hashes_by_stream(stream) + logger.info('inv message lists %i objects. Of those %i are new to me. It took %f seconds to figure that out.', len(items), len(self.objectsNewToMe), time.time()-startTime) + + payload = addresses.encodeVarint(len(self.objectsNewToMe)) + ''.join(self.objectsNewToMe.keys()) + self.append_write_buf(protocol.CreatePacket('getdata', payload)) + +# for i in random.sample(self.objectsNewToMe, len(self.objectsNewToMe)): +# DownloadQueue().put(i) + return True def bm_command_object(self): - lengthOfTimeWeShouldUseToProcessThisMessage = shared.checkAndShareObjectWithPeers(self.payload) - self.downloadQueue.task_done(calculateInventoryHash(self.payload)) + objectOffset = self.payloadOffset + nonce, expiresTime, objectType, version, streamNumber = self.decode_payload_content("QQIvv") + self.object = BMObject(nonce, expiresTime, objectType, version, streamNumber, self.payload) + + if len(self.payload) - self.payloadOffset > BMConnection.maxObjectPayloadSize: + logger.info('The payload length of this object is too large (%s bytes). Ignoring it.' % len(self.payload) - self.payloadOffset) + raise BMProtoExcessiveDataError() + + self.object.checkProofOfWorkSufficient() + self.object.checkEOLSanity() + self.object.checkStream() + + try: + if self.object.objectType == protocol.OBJECT_GETPUBKEY: + self.object.checkGetpubkey() + elif self.object.objectType == protocol.OBJECT_PUBKEY: + self.object.checkPubkey(self.payload[self.payloadOffset:self.payloadOffset+32]) + elif self.object.objectType == protocol.OBJECT_MSG: + self.object.checkMessage() + elif self.object.objectType == protocol.OBJECT_BROADCAST: + self.object.checkBroadcast(self.payload[self.payloadOffset:self.payloadOffset+32]) + # other objects don't require other types of tests + except BMObjectAlreadyHaveError: + pass + else: + Inventory()[self.object.inventoryHash] = ( + self.object.objectType, self.object.streamNumber, self.payload[objectOffset:], self.object.expiresTime, self.object.tag) + objectProcessorQueue.put((self.object.objectType,self.object.data)) + #DownloadQueue().task_done(self.object.inventoryHash) + network.connectionpool.BMConnectionPool().handleReceivedObject(self, self.object.streamNumber, self.object.inventoryHash) + #ObjUploadQueue().put(UploadElem(self.object.streamNumber, self.object.inventoryHash)) + #broadcastToSendDataQueues((streamNumber, 'advertiseobject', inventoryHash)) + return True def bm_command_addr(self): - addresses = self.decode_payload_content("lQbQ16sH") + addresses = self.decode_payload_content("lQIQ16sH") + return True def bm_command_ping(self): self.append_write_buf(protocol.CreatePacket('pong')) + return True def bm_command_pong(self): # nothing really - pass + return True def bm_command_verack(self): self.verackReceived = True if self.verackSent: if self.isSSL: self.set_state("tls_init", self.payloadLength) + self.bm_proto_reset() + return False else: - self.set_state("bm_ready", self.payloadLength) - else: - self.set_state("bm_header", self.payloadLength) - self.bm_proto_reset() - return False + self.set_connection_fully_established() + return True + return True def bm_command_version(self): #self.remoteProtocolVersion, self.services, self.timestamp, padding1, self.myExternalIP, padding2, self.remoteNodeIncomingPort = protocol.VersionPacket.unpack(self.payload[:protocol.VersionPacket.size]) @@ -223,24 +394,30 @@ class BMConnection(TLSDispatcher): print "remoteProtocolVersion: %i" % (self.remoteProtocolVersion) print "services: %08X" % (self.services) print "time offset: %i" % (self.timestamp - int(time.time())) - print "my external IP: %s" % (self.sockNode.address) + print "my external IP: %s" % (self.sockNode.host) print "remote node incoming port: %i" % (self.peerNode.port) print "user agent: %s" % (self.userAgent) if not self.peerValidityChecks(): # TODO ABORT return True + shared.connectedHostsList[self.destination] = self.streams[0] self.append_write_buf(protocol.CreatePacket('verack')) self.verackSent = True + if not self.isOutbound: + self.append_write_buf(protocol.assembleVersionMessage(self.destination.host, self.destination.port, network.connectionpool.BMConnectionPool().streams, True)) + print "%s:%i: Sending version (%ib)" % (self.destination.host, self.destination.port, len(self.write_buf)) if ((self.services & protocol.NODE_SSL == protocol.NODE_SSL) and protocol.haveSSL(not self.isOutbound)): self.isSSL = True if self.verackReceived: if self.isSSL: self.set_state("tls_init", self.payloadLength) + self.bm_proto_reset() + return False else: - self.set_state("bm_ready", self.payloadLength) - self.bm_proto_reset() - return False + self.set_connection_fully_established() + return True + return True def peerValidityChecks(self): if self.remoteProtocolVersion < 3: @@ -271,14 +448,24 @@ class BMConnection(TLSDispatcher): logger.debug ('Closed connection to %s because there is no overlapping interest in streams.', str(self.peer)) return False + if self.destination in network.connectionpool.BMConnectionPool().inboundConnections: + try: + if not protocol.checkSocksIP(self.destination.host): + self.append_write_buf(protocol.assembleErrorMessage(fatal=2, + errorText="Too many connections from your IP. Closing connection.")) + logger.debug ('Closed connection to %s because we are already connected to that IP.', + str(self.peer)) + return False + except: + pass return True def sendAddr(self): def sendChunk(): - if numberOfAddressesInAddrMessage == 0: + if addressCount == 0: return self.append_write_buf(protocol.CreatePacket('addr', \ - addresses.encodeVarint(numberOfAddressesInAddrMessage) + payload)) + addresses.encodeVarint(addressCount) + payload)) # We are going to share a maximum number of 1000 addrs (per overlapping # stream) with our peer. 500 from overlapping streams, 250 from the @@ -287,7 +474,7 @@ class BMConnection(TLSDispatcher): # init addressCount = 0 - payload = '' + payload = b'' for stream in self.streams: addrsInMyStream = {} @@ -320,42 +507,42 @@ class BMConnection(TLSDispatcher): addrsInChildStreamRight = random.sample(filtered.items(), elemCount) for (HOST, PORT), timeLastReceivedMessageFromThisNode in addrsInMyStream: addressCount += 1 - payload += pack( + payload += struct.pack( '>Q', timeLastReceivedMessageFromThisNode) # 64-bit time - payload += pack('>I', stream) - payload += pack( + payload += struct.pack('>I', stream) + payload += struct.pack( '>q', 1) # service bit flags offered by this node payload += protocol.encodeHost(HOST) - payload += pack('>H', PORT) # remote port + payload += struct.pack('>H', PORT) # remote port if addressCount >= BMConnection.maxAddrCount: sendChunk() - payload = '' + payload = b'' addressCount = 0 for (HOST, PORT), timeLastReceivedMessageFromThisNode in addrsInChildStreamLeft: addressCount += 1 - payload += pack( + payload += struct.pack( '>Q', timeLastReceivedMessageFromThisNode) # 64-bit time - payload += pack('>I', stream * 2) - payload += pack( + payload += struct.pack('>I', stream * 2) + payload += struct.pack( '>q', 1) # service bit flags offered by this node payload += protocol.encodeHost(HOST) - payload += pack('>H', PORT) # remote port + payload += struct.pack('>H', PORT) # remote port if addressCount >= BMConnection.maxAddrCount: sendChunk() - payload = '' + payload = b'' addressCount = 0 for (HOST, PORT), timeLastReceivedMessageFromThisNode in addrsInChildStreamRight: addressCount += 1 - payload += pack( + payload += struct.pack( '>Q', timeLastReceivedMessageFromThisNode) # 64-bit time - payload += pack('>I', (stream * 2) + 1) - payload += pack( + payload += struct.pack('>I', (stream * 2) + 1) + payload += struct.pack( '>q', 1) # service bit flags offered by this node payload += protocol.encodeHost(HOST) - payload += pack('>H', PORT) # remote port + payload += struct.pack('>H', PORT) # remote port if addressCount >= BMConnection.maxAddrCount: sendChunk() - payload = '' + payload = b'' addressCount = 0 # flush @@ -365,19 +552,21 @@ class BMConnection(TLSDispatcher): def sendChunk(): if objectCount == 0: return - payload = encodeVarint(objectCount) + payload - logger.debug('Sending huge inv message with %i objects to just this one peer', - str(numberOfObjects)) - self.append_write_buf(protocol.CreatePacket('inv', payload)) + logger.debug('Sending huge inv message with %i objects to just this one peer', objectCount) + self.append_write_buf(protocol.CreatePacket('inv', addresses.encodeVarint(objectCount) + payload)) # Select all hashes for objects in this stream. bigInvList = {} for stream in self.streams: for hash in Inventory().unexpired_hashes_by_stream(stream): - if not self.objectHashHolderInstance.hasHash(hash): - bigInvList[hash] = 0 + bigInvList[hash] = 0 +# for hash in ObjUploadQueue().streamHashes(stream): +# try: +# del bigInvList[hash] +# except KeyError: +# pass objectCount = 0 - payload = '' + payload = b'' # Now let us start appending all of these hashes together. They will be # sent out in a big inv message to our new peer. for hash, storedValue in bigInvList.items(): @@ -385,12 +574,43 @@ class BMConnection(TLSDispatcher): objectCount += 1 if objectCount >= BMConnection.maxObjectCount: self.sendChunk() - payload = '' + payload = b'' objectCount = 0 # flush sendChunk() + def handle_connect_event(self): + try: + asyncore.dispatcher.handle_connect_event(self) + self.connectedAt = time.time() + except socket.error as e: + print "%s:%i: socket error: %s" % (self.destination.host, self.destination.port, str(e)) + self.close() + + def handle_read_event(self): + try: + asyncore.dispatcher.handle_read_event(self) + except socket.error as e: + print "%s:%i: socket error: %s" % (self.destination.host, self.destination.port, str(e)) + self.close() + + def handle_write_event(self): + try: + asyncore.dispatcher.handle_write_event(self) + except socket.error as e: + print "%s:%i: socket error: %s" % (self.destination.host, self.destination.port, str(e)) + self.close() + + def close(self, reason=None): + if reason is None: + print "%s:%i: closing" % (self.destination.host, self.destination.port) + #traceback.print_stack() + else: + print "%s:%i: closing, %s" % (self.destination.host, self.destination.port, reason) + network.connectionpool.BMConnectionPool().removeConnection(self) + asyncore.dispatcher.close(self) + class Socks5BMConnection(Socks5Connection, BMConnection): def __init__(self, address): @@ -411,24 +631,19 @@ class Socks4aBMConnection(Socks4aConnection, BMConnection): class BMServer(AdvancedDispatcher): - port = 8444 - - def __init__(self, port=None): + def __init__(self, host='127.0.0.1', port=8444): if not hasattr(self, '_map'): AdvancedDispatcher.__init__(self) self.create_socket(socket.AF_INET, socket.SOCK_STREAM) self.set_reuse_addr() - if port is None: - port = BMServer.port - self.bind(('127.0.0.1', port)) - self.connections = 0 + self.bind((host, port)) self.listen(5) def handle_accept(self): pair = self.accept() if pair is not None: sock, addr = pair - BMConnection(sock=sock) + network.connectionpool.BMConnectionPool().addConnection(BMConnection(sock=sock)) if __name__ == "__main__": diff --git a/src/network/bmqueues.py b/src/network/bmqueues.py new file mode 100644 index 00000000..96ad52e4 --- /dev/null +++ b/src/network/bmqueues.py @@ -0,0 +1,95 @@ +import time + +from inventory import Inventory +from network.downloadqueue import DownloadQueue +from network.uploadqueue import UploadQueue + +haveBloom = False + +try: + # pybloomfiltermmap + from pybloomfilter import BloomFilter + haveBloom = True +except ImportError: + try: + # pybloom + from pybloom import BloomFilter + haveBloom = True + except ImportError: + pass + +# it isn't actually implemented yet so no point in turning it on +haveBloom = False + +class BMQueues(object): + invCleanPeriod = 300 + invInitialCapacity = 50000 + invErrorRate = 0.03 + + def __init__(self): + self.objectsNewToMe = {} + self.objectsNewToThem = {} + self.initInvBloom() + self.initAddrBloom() + + def initInvBloom(self): + if haveBloom: + # lock? + self.invBloom = BloomFilter(capacity=BMQueues.invInitialCapacity, + error_rate=BMQueues.invErrorRate) + + def initAddrBloom(self): + if haveBloom: + # lock? + self.addrBloom = BloomFilter(capacity=BMQueues.invInitialCapacity, + error_rate=BMQueues.invErrorRate) + + def clean(self): + if self.lastcleaned < time.time() - BMQueues.invCleanPeriod: + if haveBloom: + if PendingDownloadQueue().size() == 0: + self.initInvBloom() + self.initAddrBloom() + else: + # release memory + self.objectsNewToMe = self.objectsNewToMe.copy() + self.objectsNewToThem = self.objectsNewToThem.copy() + + def hasObj(self, hashid): + if haveBloom: + return hashid in self.invBloom + else: + return hashid in self.objectsNewToMe + + def handleReceivedObj(self, hashid): + if haveBloom: + self.invBloom.add(hashid) + elif hashid in Inventory(): + try: + del self.objectsNewToThem[hashid] + except KeyError: + pass + else: + self.objectsNewToMe[hashid] = True + + def hasAddr(self, addr): + if haveBloom: + return addr in self.invBloom + + def addAddr(self, hashid): + if haveBloom: + self.addrBloom.add(hashid) + +# addr sending -> per node upload queue, and flush every minute or so +# inv sending -> if not in bloom, inv immediately, otherwise put into a per node upload queue and flush every minute or so + +# no bloom +# - if inv arrives +# - if we don't have it, add tracking and download queue +# - if we do have it, remove from tracking +# tracking downloads +# - per node hash of items the node has but we don't +# tracking inv +# - per node hash of items that neither the remote node nor we have +# + diff --git a/src/network/connectionchooser.py b/src/network/connectionchooser.py new file mode 100644 index 00000000..a1b1d10b --- /dev/null +++ b/src/network/connectionchooser.py @@ -0,0 +1,11 @@ +import random + +from bmconfigparser import BMConfigParser +import knownnodes +import state + +def chooseConnection(stream): + if state.trustedPeer: + return state.trustedPeer + else: + return random.choice(knownnodes.knownNodes[stream].keys()) diff --git a/src/network/connectionpool.py b/src/network/connectionpool.py new file mode 100644 index 00000000..e79f033c --- /dev/null +++ b/src/network/connectionpool.py @@ -0,0 +1,149 @@ +import errno +import socket +import time +import random + +from bmconfigparser import BMConfigParser +from debug import logger +import helper_bootstrap +import network.bmproto +from network.connectionchooser import chooseConnection +import network.asyncore_pollchoose as asyncore +import protocol +from singleton import Singleton +import shared +import state + +@Singleton +class BMConnectionPool(object): + def __init__(self): + asyncore.set_rates( + BMConfigParser().safeGetInt("bitmessagesettings", "maxdownloadrate"), + BMConfigParser().safeGetInt("bitmessagesettings", "maxuploadrate")) + self.outboundConnections = {} + self.inboundConnections = {} + self.listeningSockets = {} + self.streams = [] + + self.bootstrapped = False + + def handleReceivedObject(self, connection, streamNumber, hashid): + for i in self.inboundConnections.values() + self.outboundConnections.values(): + if not isinstance(i, network.bmproto.BMConnection): + continue + if i == connection: + try: + del i.objectsNewToThem[hashid] + except KeyError: + pass + else: + try: + del i.objectsNewToThem[hashid] + except KeyError: + i.objectsNewToThem[hashid] = True + try: + del i.objectsNewToMe[hashid] + except KeyError: + pass + + def connectToStream(self, streamNumber): + self.streams.append(streamNumber) + + def addConnection(self, connection): + if connection.isOutbound: + self.outboundConnections[connection.destination] = connection + else: + if connection.destination.host in self.inboundConnections: + self.inboundConnections[connection.destination] = connection + else: + self.inboundConnections[connection.destination.host] = connection + + def removeConnection(self, connection): + if connection.isOutbound: + try: + del self.outboundConnections[connection.destination] + except KeyError: + pass + else: + try: + del self.inboundConnections[connection.destination] + except KeyError: + try: + del self.inboundConnections[connection.destination.host] + except KeyError: + pass + + def startListening(self): + port = BMConfigParser().safeGetInt("bitmessagesettings", "port") + if BMConfigParser().safeGet("bitmessagesettings", "onionhostname").endswith(".onion"): + host = BMConfigParser().safeGet("bitmessagesettigns", "onionbindip") + else: + host = '127.0.0.1' + if BMConfigParser().safeGetBoolean("bitmessagesettings", "sockslisten") or \ + BMConfigParser().get("bitmessagesettings", "socksproxytype") == "none": + host = '' + self.listeningSockets[state.Peer(host, port)] = network.bmproto.BMServer(host=host, port=port) + + def loop(self): + # defaults to empty loop if outbound connections are maxed + spawnConnections = False + acceptConnections = True + if BMConfigParser().safeGetBoolean('bitmessagesettings', 'dontconnect'): + acceptConnections = False + else: + spawnConnections = True + if BMConfigParser().safeGetBoolean('bitmessagesettings', 'sendoutgoingconnections'): + spawnConnections = True + if BMConfigParser().get('bitmessagesettings', 'socksproxytype')[0:5] == 'SOCKS' and \ + (not BMConfigParser().getboolean('bitmessagesettings', 'sockslisten') and \ + ".onion" not in BMConfigParser().get('bitmessagesettings', 'onionhostname')): + acceptConnections = False + + spawnConnections = False + if spawnConnections: + if not self.bootstrapped: + print "bootstrapping dns" + helper_bootstrap.dns() + self.bootstrapped = True + for i in range(len(self.outboundConnections), BMConfigParser().safeGetInt("bitmessagesettings", "maxoutboundconnections")): + chosen = chooseConnection(random.choice(self.streams)) + if chosen in self.outboundConnections: + continue + if chosen.host in self.inboundConnections: + continue + + #for c in self.outboundConnections: + # if chosen == c.destination: + # continue + #for c in self.inboundConnections: + # if chosen.host == c.destination.host: + # continue + try: + if (BMConfigParser().safeGet("bitmessagesettings", "socksproxytype") == "SOCKS5"): + self.addConnection(network.bmproto.Socks5BMConnection(chosen)) + elif (BMConfigParser().safeGet("bitmessagesettings", "socksproxytype") == "SOCKS4a"): + self.addConnection(network.bmproto.Socks4aBMConnection(chosen)) + elif not chosen.host.endswith(".onion"): + self.addConnection(network.bmproto.BMConnection(chosen)) + except socket.error as e: + if e.errno == errno.ENETUNREACH: + continue + + if acceptConnections and len(self.listeningSockets) == 0: + self.startListening() + logger.info('Listening for incoming connections.') + if len(self.listeningSockets) > 0 and not acceptConnections: + for i in self.listeningSockets: + i.close() + logger.info('Stopped listening for incoming connections.') + +# while len(asyncore.socket_map) > 0 and state.shutdown == 0: +# print "loop, state = %s" % (proxy.state) + asyncore.loop(timeout=2.0, count=1) + + for i in self.inboundConnections.values() + self.outboundConnections.values(): + minTx = time.time() - 20 + if i.connectionFullyEstablished: + minTx -= 300 - 20 + if i.lastTx < minTx: + i.close("Timeout (%is)" % (time.time() - i.lastTx)) diff --git a/src/network/downloadqueue.py b/src/network/downloadqueue.py new file mode 100644 index 00000000..3789fb19 --- /dev/null +++ b/src/network/downloadqueue.py @@ -0,0 +1,12 @@ +#import collections +from threading import current_thread, enumerate as threadingEnumerate, RLock +import Queue +import time + +#from helper_sql import * +from singleton import Singleton + +@Singleton +class DownloadQueue(Queue.Queue): + # keep a track of objects that have been advertised to us but we haven't downloaded them yet + maxWait = 300 diff --git a/src/network/networkthread.py b/src/network/networkthread.py new file mode 100644 index 00000000..3a9d8e37 --- /dev/null +++ b/src/network/networkthread.py @@ -0,0 +1,40 @@ +import threading + +from bmconfigparser import BMConfigParser +from debug import logger +from helper_threading import StoppableThread +import network.asyncore_pollchoose as asyncore +from network.connectionpool import BMConnectionPool + +class BMNetworkThread(threading.Thread, StoppableThread): + def __init__(self): + threading.Thread.__init__(self, name="BMNetworkThread") + self.initStop() + self.name = "AsyncoreThread" + BMConnectionPool() + logger.error("init asyncore thread") + + def run(self): + while not self._stopped: + BMConnectionPool().loop() + + def stopThread(self): + super(BMNetworkThread, self).stopThread() + for i in BMConnectionPool().listeningSockets: + try: + i.close() + except: + pass + for i in BMConnectionPool().outboundConnections: + try: + i.close() + except: + pass + for i in BMConnectionPool().inboundConnections: + try: + i.close() + except: + pass + + # just in case + asyncore.close_all() diff --git a/src/network/node.py b/src/network/node.py index 054d07d8..ab9f5fbe 100644 --- a/src/network/node.py +++ b/src/network/node.py @@ -1,66 +1,3 @@ -import time - -from inventory import PendingDownloadQueue - -try: - # pybloomfiltermmap - from pybloomfilter import BloomFilter -except ImportError: - try: - # pybloom - from pybloom import BloomFilter - except ImportError: - # bundled pybloom - from fallback.pybloom import BloomFilter - - -class Node(object): - invCleanPeriod = 300 - invInitialCapacity = 50000 - invErrorRate = 0.03 - - def __init__(self): - self.initInvBloom() - self.initAddrBloom() - - def initInvBloom(self): - # lock? - self.invBloom = BloomFilter(capacity=Node.invInitialCapacity, - error_rate=Node.invErrorRate) - - def initAddrBloom(self): - # lock? - self.addrBloom = BloomFilter(capacity=Node.invInitialCapacity, - error_rate=Node.invErrorRate) - - def cleanBloom(self): - if self.lastcleaned < time.time() - Node.invCleanPeriod: - if PendingDownloadQueue().size() == 0: - self.initInvBloom() - self.initAddrBloom() - - def hasInv(self, hashid): - return hashid in self.invBloom - - def addInv(self, hashid): - self.invBloom.add(hashid) - - def hasAddr(self, hashid): - return hashid in self.invBloom - - def addInv(self, hashid): - self.invBloom.add(hashid) - -# addr sending -> per node upload queue, and flush every minute or so -# inv sending -> if not in bloom, inv immediately, otherwise put into a per node upload queue and flush every minute or so - -# no bloom -# - if inv arrives -# - if we don't have it, add tracking and download queue -# - if we do have it, remove from tracking -# tracking downloads -# - per node hash of items the node has but we don't -# tracking inv -# - per node hash of items that neither the remote node nor we have -# +import collections +Node = collections.namedtuple('Node', ['services', 'host', 'port']) diff --git a/src/network/tls.py b/src/network/tls.py index c7554891..669d9aa3 100644 --- a/src/network/tls.py +++ b/src/network/tls.py @@ -60,14 +60,14 @@ class TLSDispatcher(AdvancedDispatcher): def writable(self): if self.tlsStarted and not self.tlsDone and len(self.write_buf) == 0: - print "tls writable, %r" % (self.want_write) + #print "tls writable, %r" % (self.want_write) return self.want_write else: return AdvancedDispatcher.writable(self) def readable(self): if self.tlsStarted and not self.tlsDone and len(self.write_buf) == 0: - print "tls readable, %r" % (self.want_read) + #print "tls readable, %r" % (self.want_read) return self.want_read else: return AdvancedDispatcher.readable(self) @@ -75,19 +75,19 @@ class TLSDispatcher(AdvancedDispatcher): def handle_read(self): # wait for write buffer flush if self.tlsStarted and not self.tlsDone and len(self.write_buf) == 0: - print "handshaking (read)" + #print "handshaking (read)" self.state_tls_handshake() else: - print "not handshaking (read)" + #print "not handshaking (read)" return AdvancedDispatcher.handle_read(self) def handle_write(self): # wait for write buffer flush if self.tlsStarted and not self.tlsDone and len(self.write_buf) == 0: - print "handshaking (write)" + #print "handshaking (write)" self.state_tls_handshake() else: - print "not handshaking (write)" + #print "not handshaking (write)" return AdvancedDispatcher.handle_write(self) def state_tls_handshake(self): @@ -96,24 +96,25 @@ class TLSDispatcher(AdvancedDispatcher): return False # Perform the handshake. try: - print "handshaking (internal)" + #print "handshaking (internal)" self.sslSocket.do_handshake() except ssl.SSLError, err: - print "handshake fail" + #print "%s:%i: handshake fail" % (self.destination.host, self.destination.port) self.want_read = self.want_write = False if err.args[0] == ssl.SSL_ERROR_WANT_READ: - print "want read" + #print "want read" self.want_read = True - elif err.args[0] == ssl.SSL_ERROR_WANT_WRITE: - print "want write" + if err.args[0] == ssl.SSL_ERROR_WANT_WRITE: + #print "want write" self.want_write = True - else: + if not (self.want_write or self.want_read): raise else: - print "handshake success" + print "%s:%i: handshake success" % (self.destination.host, self.destination.port) # The handshake has completed, so remove this channel and... self.del_channel() self.set_socket(self.sslSocket) self.tlsDone = True - self.state_bm_ready() + self.set_state("bm_header") + self.set_connection_fully_established() return False diff --git a/src/network/uploadqueue.py b/src/network/uploadqueue.py new file mode 100644 index 00000000..5d699e96 --- /dev/null +++ b/src/network/uploadqueue.py @@ -0,0 +1,70 @@ +from collections import namedtuple +import Queue +import random +from threading import current_thread, enumerate as threadingEnumerate, RLock +import time + +#from helper_sql import * +from singleton import Singleton + +UploadElem = namedtuple("UploadElem", "stream identifier") + +class UploadQueueDeadlineException(Exception): + pass + + +class UploadQueue(object): + queueCount = 10 + + def __init__(self): + self.queue = [] + self.lastGet = 0 + self.getIterator = 0 + for i in range(UploadQueue.queueCount): + self.queue.append([]) + + def put(self, item): + self.queue[random.randrange(0, UploadQueue.queueCount)].append(item) + + def get(self): + i = UploadQueue.queueCount + retval = [] + while self.lastGet < time.time() - 1 and i > 0: + if len(self.queue) > 0: + retval.extend(self.queue[self.getIterator]) + self.queue[self.getIterator] = [] + self.lastGet += 1 + # only process each queue once + i -= 1 + self.getIterator = (self.getIterator + 1) % UploadQueue.queueCount + if self.lastGet < time.time() - 1: + self.lastGet = time.time() + return retval + + def streamElems(self, stream): + retval = {} + for q in self.queue: + for elem in q: + if elem.stream == stream: + retval[elem.identifier] = True + return retval + + def len(self): + retval = 0 + for i in range(UploadQueue.queueCount): + retval += len(self.queue[i]) + return retval + + def stop(self): + for i in range(UploadQueue.queueCount): + self.queue[i] = [] + + +@Singleton +class AddrUploadQueue(UploadQueue): + pass + + +@Singleton +class ObjUploadQueue(UploadQueue): + pass diff --git a/src/protocol.py b/src/protocol.py index 9397cd8b..b7847e8f 100644 --- a/src/protocol.py +++ b/src/protocol.py @@ -32,6 +32,12 @@ STATUS_WARNING = 0 STATUS_ERROR = 1 STATUS_FATAL = 2 +#Object types +OBJECT_GETPUBKEY = 0 +OBJECT_PUBKEY = 1 +OBJECT_MSG = 2 +OBJECT_BROADCAST = 3 + eightBytesOfRandomDataUsedToDetectConnectionsToSelf = pack( '>Q', random.randrange(1, 18446744073709551615)) From bafdd6a93ac3e581c2be0065d3b36348fedf2fe3 Mon Sep 17 00:00:00 2001 From: Peter Surda Date: Wed, 24 May 2017 16:54:33 +0200 Subject: [PATCH 049/407] Allow making outbound connections in asyncore --- src/network/connectionpool.py | 1 - 1 file changed, 1 deletion(-) diff --git a/src/network/connectionpool.py b/src/network/connectionpool.py index e79f033c..7036e7cf 100644 --- a/src/network/connectionpool.py +++ b/src/network/connectionpool.py @@ -99,7 +99,6 @@ class BMConnectionPool(object): ".onion" not in BMConfigParser().get('bitmessagesettings', 'onionhostname')): acceptConnections = False - spawnConnections = False if spawnConnections: if not self.bootstrapped: print "bootstrapping dns" From fa56ab3e6fb73c45707c70a9ed0f09e44d928f7a Mon Sep 17 00:00:00 2001 From: Peter Surda Date: Wed, 24 May 2017 21:15:36 +0200 Subject: [PATCH 050/407] Asyncore update - better error handling - bug fixes - remove some debug output --- src/network/asyncore_pollchoose.py | 17 +++++++++-------- src/network/bmproto.py | 10 +++++++--- src/network/tls.py | 2 +- 3 files changed, 17 insertions(+), 12 deletions(-) diff --git a/src/network/asyncore_pollchoose.py b/src/network/asyncore_pollchoose.py index b26d4cab..72a829ce 100644 --- a/src/network/asyncore_pollchoose.py +++ b/src/network/asyncore_pollchoose.py @@ -57,6 +57,7 @@ import warnings import os from errno import EALREADY, EINPROGRESS, EWOULDBLOCK, ECONNRESET, EINVAL, \ ENOTCONN, ESHUTDOWN, EISCONN, EBADF, ECONNABORTED, EPIPE, EAGAIN, \ + ECONNREFUSED, \ errorcode try: from errno import WSAEWOULDBLOCK @@ -65,7 +66,7 @@ except: from ssl import SSLError, SSL_ERROR_WANT_READ, SSL_ERROR_WANT_WRITE _DISCONNECTED = frozenset((ECONNRESET, ENOTCONN, ESHUTDOWN, ECONNABORTED, EPIPE, - EBADF)) + EBADF, ECONNREFUSED)) OP_READ = 1 OP_WRITE = 2 @@ -530,7 +531,7 @@ class dispatcher: except TypeError: return None except socket.error as why: - if why.args[0] in (EWOULDBLOCK, ECONNABORTED, EAGAIN): + if why.args[0] in (EWOULDBLOCK, ECONNABORTED, EAGAIN, ENOTCONN): return None else: raise @@ -547,11 +548,11 @@ class dispatcher: else: raise except socket.error as why: - if why.errno in (errno.EAGAIN, errno.EWOULDBLOCK) or \ + if why.args[0] in (EAGAIN, EWOULDBLOCK) or \ (sys.platform.startswith('win') and \ - err.errno == errno.WSAEWOULDBLOCK): + err.errno == WSAEWOULDBLOCK): return 0 - elif why.errno in _DISCONNECTED: + elif why.args[0] in _DISCONNECTED: self.handle_close() return 0 else: @@ -574,11 +575,11 @@ class dispatcher: raise except socket.error as why: # winsock sometimes raises ENOTCONN - if why.errno in (errno.EAGAIN, errno.EWOULDBLOCK) or \ + if why.args[0] in (EAGAIN, EWOULDBLOCK) or \ (sys.platform.startswith('win') and \ - err.errno == errno.WSAEWOULDBLOCK): + err.errno == WSAEWOULDBLOCK): return b'' - if why.errno in _DISCONNECTED: + if why.args[0] in _DISCONNECTED: self.handle_close() return b'' else: diff --git a/src/network/bmproto.py b/src/network/bmproto.py index d9ed2a95..9a697b13 100644 --- a/src/network/bmproto.py +++ b/src/network/bmproto.py @@ -289,7 +289,7 @@ class BMConnection(TLSDispatcher, BMQueues): # print "skipping getdata" # return True for i in items: - print "received getdata request for item %s" % (hexlify(i)) + #print "received getdata request for item %s" % (hexlify(i)) #logger.debug('received getdata request for item:' + hexlify(i)) #if i in ObjUploadQueue.streamElems(1): if False: @@ -309,7 +309,8 @@ class BMConnection(TLSDispatcher, BMQueues): logger.error("Too many items in inv message!") raise BMProtoExcessiveDataError() else: - print "items in inv: %i" % (len(items)) + pass + #print "items in inv: %i" % (len(items)) startTime = time.time() #advertisedSet = set() @@ -609,7 +610,10 @@ class BMConnection(TLSDispatcher, BMQueues): else: print "%s:%i: closing, %s" % (self.destination.host, self.destination.port, reason) network.connectionpool.BMConnectionPool().removeConnection(self) - asyncore.dispatcher.close(self) + try: + asyncore.dispatcher.close(self) + except AttributeError: + pass class Socks5BMConnection(Socks5Connection, BMConnection): diff --git a/src/network/tls.py b/src/network/tls.py index 669d9aa3..e691006d 100644 --- a/src/network/tls.py +++ b/src/network/tls.py @@ -110,7 +110,7 @@ class TLSDispatcher(AdvancedDispatcher): if not (self.want_write or self.want_read): raise else: - print "%s:%i: handshake success" % (self.destination.host, self.destination.port) + print "%s:%i: TLS handshake success%s" % (self.destination.host, self.destination.port, ", TLS protocol version: %s" % (self.sslSocket.version()) if sys.version_info >= (2, 7, 9) else "") # The handshake has completed, so remove this channel and... self.del_channel() self.set_socket(self.sslSocket) From edcba9982bfd291e9a0c4b69c9026ed29b6dd722 Mon Sep 17 00:00:00 2001 From: Peter Surda Date: Wed, 24 May 2017 21:35:50 +0200 Subject: [PATCH 051/407] Asyncore getdata processing performance improvement - no need to query DB for existence of each entry --- src/network/bmproto.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/network/bmproto.py b/src/network/bmproto.py index 9a697b13..bf7019d3 100644 --- a/src/network/bmproto.py +++ b/src/network/bmproto.py @@ -295,9 +295,10 @@ class BMConnection(TLSDispatcher, BMQueues): if False: self.antiIntersectionDelay() else: - if i in Inventory(): + try: self.append_write_buf(protocol.CreatePacket('object', Inventory()[i].payload)) - else: + # this is faster than "if i in Inventory()" + except KeyError: self.antiIntersectionDelay() logger.warning('%s asked for an object with a getdata which is not in either our memory inventory or our SQL inventory. We probably cleaned it out after advertising it but before they got around to asking for it.' % (self.peer,)) return True From 9683c879bc778135a3bdb233c1258771f652ab7d Mon Sep 17 00:00:00 2001 From: Peter Surda Date: Thu, 25 May 2017 14:59:18 +0200 Subject: [PATCH 052/407] Asyncore update - Network status UI works but current speed isn't implemented yet - Track per connection and global transferred bytes - Add locking to write queue so that other threads can put stuff there - send ping on timeout (instead of closing the connection) - implement open port checker (untested, never triggered yet) - error handling on IO --- src/bitmessageqt/networkstatus.py | 15 +++++---- src/network/advanceddispatcher.py | 18 +++++++--- src/network/asyncore_pollchoose.py | 12 +++++-- src/network/bmproto.py | 17 ++++++---- src/network/connectionchooser.py | 7 +++- src/network/connectionpool.py | 5 ++- src/network/stats.py | 43 ++++++++++++++++++++++++ src/network/tls.py | 54 ++++++++++++++++++------------ src/queues.py | 1 + 9 files changed, 130 insertions(+), 42 deletions(-) create mode 100644 src/network/stats.py diff --git a/src/bitmessageqt/networkstatus.py b/src/bitmessageqt/networkstatus.py index b5870a6b..158f02fa 100644 --- a/src/bitmessageqt/networkstatus.py +++ b/src/bitmessageqt/networkstatus.py @@ -5,10 +5,10 @@ import shared from tr import _translate from inventory import Inventory, PendingDownloadQueue, PendingUpload import l10n +import network.stats from retranslateui import RetranslateMixin from uisignaler import UISignaler import widgets -import throttle class NetworkStatus(QtGui.QWidget, RetranslateMixin): @@ -69,14 +69,15 @@ class NetworkStatus(QtGui.QWidget, RetranslateMixin): sent and received by 2. """ self.labelBytesRecvCount.setText(_translate( - "networkstatus", "Down: %1/s Total: %2").arg(self.formatByteRate(throttle.ReceiveThrottle().getSpeed()), self.formatBytes(throttle.ReceiveThrottle().total))) + "networkstatus", "Down: %1/s Total: %2").arg(self.formatByteRate(network.stats.downloadSpeed()), self.formatBytes(network.stats.receivedBytes()))) self.labelBytesSentCount.setText(_translate( - "networkstatus", "Up: %1/s Total: %2").arg(self.formatByteRate(throttle.SendThrottle().getSpeed()), self.formatBytes(throttle.SendThrottle().total))) + "networkstatus", "Up: %1/s Total: %2").arg(self.formatByteRate(network.stats.uploadSpeed()), self.formatBytes(network.stats.sentBytes()))) def updateNetworkStatusTab(self): totalNumberOfConnectionsFromAllStreams = 0 # One would think we could use len(sendDataQueues) for this but the number doesn't always match: just because we have a sendDataThread running doesn't mean that the connection has been fully established (with the exchange of version messages). streamNumberTotals = {} - for host, streamNumber in shared.connectedHostsList.items(): + connectedHosts = network.stats.connectedHostsList() + for host, streamNumber in connectedHosts: if not streamNumber in streamNumberTotals: streamNumberTotals[streamNumber] = 1 else: @@ -114,10 +115,10 @@ class NetworkStatus(QtGui.QWidget, RetranslateMixin): self.tableWidgetConnectionCount.setItem(0,1,newItem) totalNumberOfConnectionsFromAllStreams += connectionCount""" self.labelTotalConnections.setText(_translate( - "networkstatus", "Total Connections: %1").arg(str(len(shared.connectedHostsList)))) - if len(shared.connectedHostsList) > 0 and shared.statusIconColor == 'red': # FYI: The 'singlelistener' thread sets the icon color to green when it receives an incoming connection, meaning that the user's firewall is configured correctly. + "networkstatus", "Total Connections: %1").arg(str(len(connectedHosts)))) + if len(connectedHosts) > 0 and shared.statusIconColor == 'red': # FYI: The 'singlelistener' thread sets the icon color to green when it receives an incoming connection, meaning that the user's firewall is configured correctly. self.window().setStatusIcon('yellow') - elif len(shared.connectedHostsList) == 0: + elif len(connectedHosts) == 0: self.window().setStatusIcon('red') # timer driven diff --git a/src/network/advanceddispatcher.py b/src/network/advanceddispatcher.py index f4ba120e..d1b5f567 100644 --- a/src/network/advanceddispatcher.py +++ b/src/network/advanceddispatcher.py @@ -1,3 +1,4 @@ +from threading import RLock import time import asyncore_pollchoose as asyncore @@ -11,15 +12,20 @@ class AdvancedDispatcher(asyncore.dispatcher): asyncore.dispatcher.__init__(self, sock) self.read_buf = b"" self.write_buf = b"" + self.writeLock = RLock() self.state = "init" self.lastTx = time.time() + self.sentBytes = 0 + self.receivedBytes = 0 def append_write_buf(self, string = None): - self.write_buf += string + with self.writeLock: + self.write_buf += string def slice_write_buf(self, length=0): if length > 0: - self.write_buf = self.write_buf[length:] + with self.writeLock: + self.write_buf = self.write_buf[length:] def slice_read_buf(self, length=0): if length > 0: @@ -58,9 +64,11 @@ class AdvancedDispatcher(asyncore.dispatcher): if asyncore.maxDownloadRate > 0: newData = self.recv(asyncore.downloadChunk) asyncore.downloadBucket -= len(newData) - self.read_buf += newData else: - self.read_buf += self.recv(AdvancedDispatcher._buf_len) + newData = self.recv(AdvancedDispatcher._buf_len) + self.receivedBytes += len(newData) + asyncore.updateReceived(len(newData)) + self.read_buf += newData self.process() def handle_write(self): @@ -70,6 +78,8 @@ class AdvancedDispatcher(asyncore.dispatcher): asyncore.uploadBucket -= written else: written = self.send(self.write_buf) + asyncore.updateSent(written) + self.sentBytes += written self.slice_write_buf(written) def handle_connect(self): diff --git a/src/network/asyncore_pollchoose.py b/src/network/asyncore_pollchoose.py index 72a829ce..08ee42d0 100644 --- a/src/network/asyncore_pollchoose.py +++ b/src/network/asyncore_pollchoose.py @@ -93,10 +93,12 @@ maxDownloadRate = 0 downloadChunk = 0 downloadTimestamp = 0 downloadBucket = 0 +receivedBytes = 0 maxUploadRate = 0 uploadChunk = 0 uploadTimestamp = 0 uploadBucket = 0 +sentBytes = 0 def read(obj): try: @@ -127,6 +129,14 @@ def set_rates(download, upload): downloadTimestamp = time.time() uploadTimestamp = time.time() +def updateReceived(download=0): + global receivedBytes + receivedBytes += download + +def updateSent(upload=0): + global sentBytes + sentBytes += upload + def wait_tx_buckets(): global downloadBucket, uploadBucket, downloadTimestamp, uploadTimestamp if maxDownloadRate > 0 and maxUploadRate > 0: @@ -150,8 +160,6 @@ def wait_tx_buckets(): uploadBucket += (time.time() - uploadTimestamp) * maxUploadRate uploadTimestamp = time.time() - - def _exception(obj): try: obj.handle_expt_event() diff --git a/src/network/bmproto.py b/src/network/bmproto.py index bf7019d3..3a5ea1b4 100644 --- a/src/network/bmproto.py +++ b/src/network/bmproto.py @@ -28,7 +28,7 @@ from network.tls import TLSDispatcher import addresses from bmconfigparser import BMConfigParser -from queues import objectProcessorQueue +from queues import objectProcessorQueue, portCheckerQueue, UISignalQueue import shared import state import protocol @@ -57,6 +57,7 @@ class BMConnection(TLSDispatcher, BMQueues): self.verackReceived = False self.verackSent = False self.lastTx = time.time() + self.streams = [0] self.connectionFullyEstablished = False self.connectedAt = 0 self.skipUntil = 0 @@ -79,6 +80,7 @@ class BMConnection(TLSDispatcher, BMQueues): print "connecting in background to %s:%i" % (self.destination.host, self.destination.port) shared.connectedHostsList[self.destination] = 0 BMQueues.__init__(self) + UISignalQueue.put(('updateNetworkStatusTab', 'no data')) def bm_proto_reset(self): self.magic = None @@ -115,6 +117,7 @@ class BMConnection(TLSDispatcher, BMQueues): self.skipUntil = time.time() + now def set_connection_fully_established(self): + UISignalQueue.put(('updateNetworkStatusTab', 'no data')) self.antiIntersectionDelay(True) self.connectionFullyEstablished = True self.sendAddr() @@ -369,6 +372,10 @@ class BMConnection(TLSDispatcher, BMQueues): addresses = self.decode_payload_content("lQIQ16sH") return True + def bm_command_portcheck(self): + portCheckerQueue.put(state.Peer(self.destination, self.peerNode.port)) + return True + def bm_command_ping(self): self.append_write_buf(protocol.CreatePacket('pong')) return True @@ -399,10 +406,11 @@ class BMConnection(TLSDispatcher, BMQueues): print "my external IP: %s" % (self.sockNode.host) print "remote node incoming port: %i" % (self.peerNode.port) print "user agent: %s" % (self.userAgent) + print "streams: [%s]" % (",".join(map(str,self.streams))) if not self.peerValidityChecks(): # TODO ABORT return True - shared.connectedHostsList[self.destination] = self.streams[0] + #shared.connectedHostsList[self.destination] = self.streams[0] self.append_write_buf(protocol.CreatePacket('verack')) self.verackSent = True if not self.isOutbound: @@ -611,10 +619,7 @@ class BMConnection(TLSDispatcher, BMQueues): else: print "%s:%i: closing, %s" % (self.destination.host, self.destination.port, reason) network.connectionpool.BMConnectionPool().removeConnection(self) - try: - asyncore.dispatcher.close(self) - except AttributeError: - pass + asyncore.dispatcher.close(self) class Socks5BMConnection(Socks5Connection, BMConnection): diff --git a/src/network/connectionchooser.py b/src/network/connectionchooser.py index a1b1d10b..1c8d988d 100644 --- a/src/network/connectionchooser.py +++ b/src/network/connectionchooser.py @@ -1,11 +1,16 @@ +from queues import Queue import random from bmconfigparser import BMConfigParser import knownnodes +from queues import portCheckerQueue import state def chooseConnection(stream): if state.trustedPeer: return state.trustedPeer else: - return random.choice(knownnodes.knownNodes[stream].keys()) + try: + return portCheckerQueue.get(False) + except Queue.Empty: + return random.choice(knownnodes.knownNodes[stream].keys()) diff --git a/src/network/connectionpool.py b/src/network/connectionpool.py index 7036e7cf..6acf4bb6 100644 --- a/src/network/connectionpool.py +++ b/src/network/connectionpool.py @@ -145,4 +145,7 @@ class BMConnectionPool(object): if i.connectionFullyEstablished: minTx -= 300 - 20 if i.lastTx < minTx: - i.close("Timeout (%is)" % (time.time() - i.lastTx)) + if i.connectionFullyEstablished: + i.append_write_buf(protocol.CreatePacket('ping')) + else: + i.close("Timeout (%is)" % (time.time() - i.lastTx)) diff --git a/src/network/stats.py b/src/network/stats.py new file mode 100644 index 00000000..838ef23a --- /dev/null +++ b/src/network/stats.py @@ -0,0 +1,43 @@ +from bmconfigparser import BMConfigParser +from network.connectionpool import BMConnectionPool +import asyncore_pollchoose as asyncore +import shared +import throttle + +def connectedHostsList(): + if BMConfigParser().safeGetBoolean("network", "asyncore"): + retval = [] + for i in BMConnectionPool().inboundConnections.values() + BMConnectionPool().outboundConnections.values(): + if not i.connected: + continue + try: + retval.append((i.destination, i.streams[0])) + except AttributeError: + pass + return retval + else: + return shared.connectedHostsList.items() + +def sentBytes(): + if BMConfigParser().safeGetBoolean("network", "asyncore"): + return asyncore.sentBytes + else: + return throttle.SendThrottle().total + +def uploadSpeed(): + if BMConfigParser().safeGetBoolean("network", "asyncore"): + return 0 + else: + return throttle.sendThrottle().getSpeed() + +def receivedBytes(): + if BMConfigParser().safeGetBoolean("network", "asyncore"): + return asyncore.receivedBytes + else: + return throttle.ReceiveThrottle().total + +def downloadSpeed(): + if BMConfigParser().safeGetBoolean("network", "asyncore"): + return 0 + else: + return throttle.ReceiveThrottle().getSpeed() diff --git a/src/network/tls.py b/src/network/tls.py index e691006d..d2abb6b9 100644 --- a/src/network/tls.py +++ b/src/network/tls.py @@ -59,36 +59,48 @@ class TLSDispatcher(AdvancedDispatcher): # self.socket.context.set_ecdh_curve("secp256k1") def writable(self): - if self.tlsStarted and not self.tlsDone and len(self.write_buf) == 0: - #print "tls writable, %r" % (self.want_write) - return self.want_write - else: + try: + if self.tlsStarted and not self.tlsDone and len(self.write_buf) == 0: + #print "tls writable, %r" % (self.want_write) + return self.want_write + else: + return AdvancedDispatcher.writable(self) + except AttributeError: return AdvancedDispatcher.writable(self) def readable(self): - if self.tlsStarted and not self.tlsDone and len(self.write_buf) == 0: - #print "tls readable, %r" % (self.want_read) - return self.want_read - else: + try: + if self.tlsStarted and not self.tlsDone and len(self.write_buf) == 0: + #print "tls readable, %r" % (self.want_read) + return self.want_read + else: + return AdvancedDispatcher.readable(self) + except AttributeError: return AdvancedDispatcher.readable(self) def handle_read(self): - # wait for write buffer flush - if self.tlsStarted and not self.tlsDone and len(self.write_buf) == 0: - #print "handshaking (read)" - self.state_tls_handshake() - else: - #print "not handshaking (read)" + try: + # wait for write buffer flush + if self.tlsStarted and not self.tlsDone and len(self.write_buf) == 0: + #print "handshaking (read)" + self.state_tls_handshake() + else: + #print "not handshaking (read)" + return AdvancedDispatcher.handle_read(self) + except AttributeError: return AdvancedDispatcher.handle_read(self) def handle_write(self): - # wait for write buffer flush - if self.tlsStarted and not self.tlsDone and len(self.write_buf) == 0: - #print "handshaking (write)" - self.state_tls_handshake() - else: - #print "not handshaking (write)" - return AdvancedDispatcher.handle_write(self) + try: + # wait for write buffer flush + if self.tlsStarted and not self.tlsDone and len(self.write_buf) == 0: + #print "handshaking (write)" + self.state_tls_handshake() + else: + #print "not handshaking (write)" + return AdvancedDispatcher.handle_write(self) + except AttributeError: + return AdvancedDispatcher.handle_read(self) def state_tls_handshake(self): # wait for flush diff --git a/src/queues.py b/src/queues.py index 335863e9..c6b09307 100644 --- a/src/queues.py +++ b/src/queues.py @@ -6,5 +6,6 @@ UISignalQueue = Queue.Queue() addressGeneratorQueue = Queue.Queue() # receiveDataThreads dump objects they hear on the network into this queue to be processed. objectProcessorQueue = ObjectProcessorQueue() +portCheckerQueue = Queue.Queue() apiAddressGeneratorReturnQueue = Queue.Queue( ) # The address generator thread uses this queue to get information back to the API thread. From 51e52401fececa2c760bcd23b12141f4b3e3af13 Mon Sep 17 00:00:00 2001 From: Peter Surda Date: Thu, 25 May 2017 15:00:10 +0200 Subject: [PATCH 053/407] Windows plaform check pythonic - moved to .startswith instead of 'in' - thanks @Lvl4sword --- src/bitmessagemain.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/bitmessagemain.py b/src/bitmessagemain.py index e61675cf..2c0be937 100755 --- a/src/bitmessagemain.py +++ b/src/bitmessagemain.py @@ -92,7 +92,7 @@ def connectToStream(streamNumber): a.start() def _fixWinsock(): - if not ('win32' in sys.platform) and not ('win64' in sys.platform): + if not sys.platform.startswith('win'): return # Python 2 on Windows doesn't define a wrapper for From e309a1edb3e01a3e9e9ca30a5f263e8362b63c26 Mon Sep 17 00:00:00 2001 From: Peter Surda Date: Thu, 25 May 2017 23:04:33 +0200 Subject: [PATCH 054/407] Asyncore update - separate queue for processing blocking stuff on reception - rewrote write buffer as a queue - some addr handling - number of half open connections correct --- src/bitmessagemain.py | 12 ++++-- src/network/advanceddispatcher.py | 36 +++++++++------- src/network/bmproto.py | 68 +++++++++++++++++++------------ src/network/connectionpool.py | 53 ++++++++++++------------ src/network/networkthread.py | 2 +- src/network/receivequeuethread.py | 44 ++++++++++++++++++++ src/network/socks4a.py | 24 +++++------ src/network/socks5.py | 22 +++++----- src/protocol.py | 45 +++++++++++++++++++- src/state.py | 2 + 10 files changed, 213 insertions(+), 95 deletions(-) create mode 100644 src/network/receivequeuethread.py diff --git a/src/bitmessagemain.py b/src/bitmessagemain.py index 2c0be937..c05d002a 100755 --- a/src/bitmessagemain.py +++ b/src/bitmessagemain.py @@ -52,6 +52,7 @@ from bmconfigparser import BMConfigParser from network.connectionpool import BMConnectionPool from network.networkthread import BMNetworkThread +from network.receivequeuethread import ReceiveQueueThread # Helper Functions import helper_bootstrap @@ -65,13 +66,13 @@ def connectToStream(streamNumber): if isOurOperatingSystemLimitedToHavingVeryFewHalfOpenConnections(): # Some XP and Vista systems can only have 10 outgoing connections at a time. - maximumNumberOfHalfOpenConnections = 9 + state.maximumNumberOfHalfOpenConnections = 9 else: - maximumNumberOfHalfOpenConnections = 64 + state.maximumNumberOfHalfOpenConnections = 64 try: # don't overload Tor if BMConfigParser().get('bitmessagesettings', 'socksproxytype') != 'none': - maximumNumberOfHalfOpenConnections = 4 + state.maximumNumberOfHalfOpenConnections = 4 except: pass @@ -86,7 +87,7 @@ def connectToStream(streamNumber): if BMConfigParser().safeGetBoolean("network", "asyncore"): BMConnectionPool().connectToStream(streamNumber) else: - for i in range(maximumNumberOfHalfOpenConnections): + for i in range(state.maximumNumberOfHalfOpenConnections): a = outgoingSynSender() a.setup(streamNumber, selfInitiatedConnections) a.start() @@ -252,6 +253,9 @@ class Main: asyncoreThread = BMNetworkThread() asyncoreThread.daemon = False asyncoreThread.start() + receiveQueueThread = ReceiveQueueThread() + receiveQueueThread.daemon = False + receiveQueueThread.start() connectToStream(1) diff --git a/src/network/advanceddispatcher.py b/src/network/advanceddispatcher.py index d1b5f567..fb28f3d4 100644 --- a/src/network/advanceddispatcher.py +++ b/src/network/advanceddispatcher.py @@ -1,4 +1,4 @@ -from threading import RLock +import Queue import time import asyncore_pollchoose as asyncore @@ -12,20 +12,16 @@ class AdvancedDispatcher(asyncore.dispatcher): asyncore.dispatcher.__init__(self, sock) self.read_buf = b"" self.write_buf = b"" - self.writeLock = RLock() + self.writeQueue = Queue.Queue() + self.receiveQueue = Queue.Queue() self.state = "init" self.lastTx = time.time() self.sentBytes = 0 self.receivedBytes = 0 - def append_write_buf(self, string = None): - with self.writeLock: - self.write_buf += string - def slice_write_buf(self, length=0): if length > 0: - with self.writeLock: - self.write_buf = self.write_buf[length:] + self.write_buf = self.write_buf[length:] def slice_read_buf(self, length=0): if length > 0: @@ -54,7 +50,7 @@ class AdvancedDispatcher(asyncore.dispatcher): self.state = state def writable(self): - return self.connecting or len(self.write_buf) > 0 + return self.connecting or len(self.write_buf) > 0 or not self.writeQueue.empty() def readable(self): return self.connecting or len(self.read_buf) < AdvancedDispatcher._buf_len @@ -74,18 +70,28 @@ class AdvancedDispatcher(asyncore.dispatcher): def handle_write(self): self.lastTx = time.time() if asyncore.maxUploadRate > 0: - written = self.send(self.write_buf[0:asyncore.uploadChunk]) - asyncore.uploadBucket -= written + bufSize = asyncore.uploadChunk else: - written = self.send(self.write_buf) - asyncore.updateSent(written) - self.sentBytes += written - self.slice_write_buf(written) + bufSize = self._buf_len + while len(self.write_buf) < bufSize: + try: + self.write_buf += self.writeQueue.get(False) + except Queue.Empty: + break + if len(self.write_buf) > 0: + written = self.send(self.write_buf[0:bufSize]) + asyncore.uploadBucket -= written + asyncore.updateSent(written) + self.sentBytes += written + self.slice_write_buf(written) def handle_connect(self): self.lastTx = time.time() self.process() + def state_close(self): + pass + def close(self): self.read_buf = b"" self.write_buf = b"" diff --git a/src/network/bmproto.py b/src/network/bmproto.py index 3a5ea1b4..88a8f794 100644 --- a/src/network/bmproto.py +++ b/src/network/bmproto.py @@ -58,7 +58,7 @@ class BMConnection(TLSDispatcher, BMQueues): self.verackSent = False self.lastTx = time.time() self.streams = [0] - self.connectionFullyEstablished = False + self.fullyEstablished = False self.connectedAt = 0 self.skipUntil = 0 if address is None and sock is not None: @@ -95,8 +95,8 @@ class BMConnection(TLSDispatcher, BMQueues): def state_init(self): self.bm_proto_reset() if self.isOutbound: - self.append_write_buf(protocol.assembleVersionMessage(self.destination.host, self.destination.port, network.connectionpool.BMConnectionPool().streams, False)) - print "%s:%i: Sending version (%ib)" % (self.destination.host, self.destination.port, len(self.write_buf)) + self.writeQueue.put(protocol.assembleVersionMessage(self.destination.host, self.destination.port, network.connectionpool.BMConnectionPool().streams, False)) + print "%s:%i: Sending version" % (self.destination.host, self.destination.port) self.set_state("bm_header") return True @@ -114,12 +114,12 @@ class BMConnection(TLSDispatcher, BMQueues): logger.debug("Skipping processing for %.2fs", self.skipUntil - time.time()) else: logger.debug("Skipping processing due to missing object for %.2fs", self.skipUntil - time.time()) - self.skipUntil = time.time() + now + self.skipUntil = time.time() + delay def set_connection_fully_established(self): UISignalQueue.put(('updateNetworkStatusTab', 'no data')) self.antiIntersectionDelay(True) - self.connectionFullyEstablished = True + self.fullyEstablished = True self.sendAddr() self.sendBigInv() @@ -135,6 +135,8 @@ class BMConnection(TLSDispatcher, BMQueues): self.bm_proto_reset() self.set_state("bm_header", 1) print "Bad magic" + self.close() + return False if self.payloadLength > BMConnection.maxMessageSize: self.invalid = True self.set_state("bm_command", protocol.Header.size) @@ -150,7 +152,7 @@ class BMConnection(TLSDispatcher, BMQueues): print "Bad checksum, ignoring" self.invalid = True retval = True - if not self.connectionFullyEstablished and self.command not in ("version", "verack"): + if not self.fullyEstablished and self.command not in ("version", "verack"): logger.error("Received command %s before connection was fully established, ignoring", self.command) self.invalid = True if not self.invalid: @@ -178,7 +180,10 @@ class BMConnection(TLSDispatcher, BMQueues): except struct.error: print "decoding error, skipping" else: - print "Skipping command %s due to invalid data" % (self.command) + #print "Skipping command %s due to invalid data" % (self.command) + print "Closing due to invalid data" % (self.command) + self.close() + return False if retval: self.set_state("bm_header", self.payloadLength) self.bm_proto_reset() @@ -298,12 +303,7 @@ class BMConnection(TLSDispatcher, BMQueues): if False: self.antiIntersectionDelay() else: - try: - self.append_write_buf(protocol.CreatePacket('object', Inventory()[i].payload)) - # this is faster than "if i in Inventory()" - except KeyError: - self.antiIntersectionDelay() - logger.warning('%s asked for an object with a getdata which is not in either our memory inventory or our SQL inventory. We probably cleaned it out after advertising it but before they got around to asking for it.' % (self.peer,)) + self.receiveQueue.put(("object", i)) return True def bm_command_inv(self): @@ -327,7 +327,7 @@ class BMConnection(TLSDispatcher, BMQueues): logger.info('inv message lists %i objects. Of those %i are new to me. It took %f seconds to figure that out.', len(items), len(self.objectsNewToMe), time.time()-startTime) payload = addresses.encodeVarint(len(self.objectsNewToMe)) + ''.join(self.objectsNewToMe.keys()) - self.append_write_buf(protocol.CreatePacket('getdata', payload)) + self.writeQueue.put(protocol.CreatePacket('getdata', payload)) # for i in random.sample(self.objectsNewToMe, len(self.objectsNewToMe)): # DownloadQueue().put(i) @@ -370,6 +370,18 @@ class BMConnection(TLSDispatcher, BMQueues): def bm_command_addr(self): addresses = self.decode_payload_content("lQIQ16sH") + import pprint + for i in addresses: + seenTime, stream, services, ip, port = i + decodedIP = protocol.checkIPAddress(ip) + if stream not in state.streamsInWhichIAmParticipating: + continue + #print "maybe adding %s in stream %i to knownnodes (%i)" % (decodedIP, stream, len(knownnodes.knownNodes[stream])) + if decodedIP is not False and seenTime > time.time() - 10800: + peer = state.Peer(decodedIP, port) + if peer in knownnodes.knownNodes[stream] and knownnodes.knownNodes[stream][peer] > seenTime: + continue + knownnodes.knownNodes[stream][peer] = seenTime return True def bm_command_portcheck(self): @@ -377,7 +389,7 @@ class BMConnection(TLSDispatcher, BMQueues): return True def bm_command_ping(self): - self.append_write_buf(protocol.CreatePacket('pong')) + self.writeQueue.put(protocol.CreatePacket('pong')) return True def bm_command_pong(self): @@ -411,11 +423,11 @@ class BMConnection(TLSDispatcher, BMQueues): # TODO ABORT return True #shared.connectedHostsList[self.destination] = self.streams[0] - self.append_write_buf(protocol.CreatePacket('verack')) + self.writeQueue.put(protocol.CreatePacket('verack')) self.verackSent = True if not self.isOutbound: - self.append_write_buf(protocol.assembleVersionMessage(self.destination.host, self.destination.port, network.connectionpool.BMConnectionPool().streams, True)) - print "%s:%i: Sending version (%ib)" % (self.destination.host, self.destination.port, len(self.write_buf)) + self.writeQueue.put(protocol.assembleVersionMessage(self.destination.host, self.destination.port, network.connectionpool.BMConnectionPool().streams, True)) + print "%s:%i: Sending version" % (self.destination.host, self.destination.port) if ((self.services & protocol.NODE_SSL == protocol.NODE_SSL) and protocol.haveSSL(not self.isOutbound)): self.isSSL = True @@ -431,20 +443,20 @@ class BMConnection(TLSDispatcher, BMQueues): def peerValidityChecks(self): if self.remoteProtocolVersion < 3: - self.append_write_buf(protocol.assembleErrorMessage(fatal=2, + self.writeQueue.put(protocol.assembleErrorMessage(fatal=2, errorText="Your is using an old protocol. Closing connection.")) logger.debug ('Closing connection to old protocol version %s, node: %s', str(self.remoteProtocolVersion), str(self.peer)) return False if self.timeOffset > 3600: - self.append_write_buf(protocol.assembleErrorMessage(fatal=2, + self.writeQueue.put(protocol.assembleErrorMessage(fatal=2, errorText="Your time is too far in the future compared to mine. Closing connection.")) logger.info("%s's time is too far in the future (%s seconds). Closing connection to it.", self.peer, self.timeOffset) shared.timeOffsetWrongCount += 1 return False elif self.timeOffset < -3600: - self.append_write_buf(protocol.assembleErrorMessage(fatal=2, + self.writeQueue.put(protocol.assembleErrorMessage(fatal=2, errorText="Your time is too far in the past compared to mine. Closing connection.")) logger.info("%s's time is too far in the past (timeOffset %s seconds). Closing connection to it.", self.peer, self.timeOffset) @@ -453,7 +465,7 @@ class BMConnection(TLSDispatcher, BMQueues): else: shared.timeOffsetWrongCount = 0 if len(self.streams) == 0: - self.append_write_buf(protocol.assembleErrorMessage(fatal=2, + self.writeQueue.put(protocol.assembleErrorMessage(fatal=2, errorText="We don't have shared stream interests. Closing connection.")) logger.debug ('Closed connection to %s because there is no overlapping interest in streams.', str(self.peer)) @@ -461,7 +473,7 @@ class BMConnection(TLSDispatcher, BMQueues): if self.destination in network.connectionpool.BMConnectionPool().inboundConnections: try: if not protocol.checkSocksIP(self.destination.host): - self.append_write_buf(protocol.assembleErrorMessage(fatal=2, + self.writeQueue.put(protocol.assembleErrorMessage(fatal=2, errorText="Too many connections from your IP. Closing connection.")) logger.debug ('Closed connection to %s because we are already connected to that IP.', str(self.peer)) @@ -474,7 +486,7 @@ class BMConnection(TLSDispatcher, BMQueues): def sendChunk(): if addressCount == 0: return - self.append_write_buf(protocol.CreatePacket('addr', \ + self.writeQueue.put(protocol.CreatePacket('addr', \ addresses.encodeVarint(addressCount) + payload)) # We are going to share a maximum number of 1000 addrs (per overlapping @@ -563,7 +575,7 @@ class BMConnection(TLSDispatcher, BMQueues): if objectCount == 0: return logger.debug('Sending huge inv message with %i objects to just this one peer', objectCount) - self.append_write_buf(protocol.CreatePacket('inv', addresses.encodeVarint(objectCount) + payload)) + self.writeQueue.put(protocol.CreatePacket('inv', addresses.encodeVarint(objectCount) + payload)) # Select all hashes for objects in this stream. bigInvList = {} @@ -613,6 +625,7 @@ class BMConnection(TLSDispatcher, BMQueues): self.close() def close(self, reason=None): + self.set_state("close") if reason is None: print "%s:%i: closing" % (self.destination.host, self.destination.port) #traceback.print_stack() @@ -653,7 +666,10 @@ class BMServer(AdvancedDispatcher): pair = self.accept() if pair is not None: sock, addr = pair - network.connectionpool.BMConnectionPool().addConnection(BMConnection(sock=sock)) + try: + network.connectionpool.BMConnectionPool().addConnection(BMConnection(sock=sock)) + except socket.errno: + pass if __name__ == "__main__": diff --git a/src/network/connectionpool.py b/src/network/connectionpool.py index 6acf4bb6..8d4f4539 100644 --- a/src/network/connectionpool.py +++ b/src/network/connectionpool.py @@ -104,29 +104,32 @@ class BMConnectionPool(object): print "bootstrapping dns" helper_bootstrap.dns() self.bootstrapped = True - for i in range(len(self.outboundConnections), BMConfigParser().safeGetInt("bitmessagesettings", "maxoutboundconnections")): - chosen = chooseConnection(random.choice(self.streams)) - if chosen in self.outboundConnections: - continue - if chosen.host in self.inboundConnections: - continue - - #for c in self.outboundConnections: - # if chosen == c.destination: - # continue - #for c in self.inboundConnections: - # if chosen.host == c.destination.host: - # continue - try: - if (BMConfigParser().safeGet("bitmessagesettings", "socksproxytype") == "SOCKS5"): - self.addConnection(network.bmproto.Socks5BMConnection(chosen)) - elif (BMConfigParser().safeGet("bitmessagesettings", "socksproxytype") == "SOCKS4a"): - self.addConnection(network.bmproto.Socks4aBMConnection(chosen)) - elif not chosen.host.endswith(".onion"): - self.addConnection(network.bmproto.BMConnection(chosen)) - except socket.error as e: - if e.errno == errno.ENETUNREACH: + established = sum(1 for c in self.outboundConnections.values() if (c.connected and c.fullyEstablished)) + pending = len(self.outboundConnections) - established + if established < BMConfigParser().safeGetInt("bitmessagesettings", "maxoutboundconnections"): + for i in range(state.maximumNumberOfHalfOpenConnections - pending): + chosen = chooseConnection(random.choice(self.streams)) + if chosen in self.outboundConnections: continue + if chosen.host in self.inboundConnections: + continue + + #for c in self.outboundConnections: + # if chosen == c.destination: + # continue + #for c in self.inboundConnections: + # if chosen.host == c.destination.host: + # continue + try: + if (BMConfigParser().safeGet("bitmessagesettings", "socksproxytype") == "SOCKS5"): + self.addConnection(network.bmproto.Socks5BMConnection(chosen)) + elif (BMConfigParser().safeGet("bitmessagesettings", "socksproxytype") == "SOCKS4a"): + self.addConnection(network.bmproto.Socks4aBMConnection(chosen)) + elif not chosen.host.endswith(".onion"): + self.addConnection(network.bmproto.BMConnection(chosen)) + except socket.error as e: + if e.errno == errno.ENETUNREACH: + continue if acceptConnections and len(self.listeningSockets) == 0: self.startListening() @@ -142,10 +145,10 @@ class BMConnectionPool(object): for i in self.inboundConnections.values() + self.outboundConnections.values(): minTx = time.time() - 20 - if i.connectionFullyEstablished: + if i.fullyEstablished: minTx -= 300 - 20 if i.lastTx < minTx: - if i.connectionFullyEstablished: - i.append_write_buf(protocol.CreatePacket('ping')) + if i.fullyEstablished: + i.writeQueue.put(protocol.CreatePacket('ping')) else: i.close("Timeout (%is)" % (time.time() - i.lastTx)) diff --git a/src/network/networkthread.py b/src/network/networkthread.py index 3a9d8e37..498bd340 100644 --- a/src/network/networkthread.py +++ b/src/network/networkthread.py @@ -8,7 +8,7 @@ from network.connectionpool import BMConnectionPool class BMNetworkThread(threading.Thread, StoppableThread): def __init__(self): - threading.Thread.__init__(self, name="BMNetworkThread") + threading.Thread.__init__(self, name="AsyncoreThread") self.initStop() self.name = "AsyncoreThread" BMConnectionPool() diff --git a/src/network/receivequeuethread.py b/src/network/receivequeuethread.py new file mode 100644 index 00000000..6405238d --- /dev/null +++ b/src/network/receivequeuethread.py @@ -0,0 +1,44 @@ +import Queue +import threading + +from bmconfigparser import BMConfigParser +from debug import logger +from helper_threading import StoppableThread +from inventory import Inventory +from network.connectionpool import BMConnectionPool +import protocol + +class ReceiveQueueThread(threading.Thread, StoppableThread): + def __init__(self): + threading.Thread.__init__(self, name="ReceiveQueueThread") + self.initStop() + self.name = "ReceiveQueueThread" + BMConnectionPool() + logger.error("init asyncore thread") + + def run(self): + while not self._stopped: + processed = 0 + for i in BMConnectionPool().inboundConnections.values() + BMConnectionPool().outboundConnections.values(): + try: + command, args = i.receiveQueue.get(False) + except Queue.Empty: + continue + processed += 1 + try: + getattr(self, "command_" + str(command))(i, args) + except AttributeError: + # missing command + raise + if processed == 0: + self.stop.wait(0.2) + + def command_object(self, connection, objHash): + try: + connection.writeQueue.put(protocol.CreatePacket('object', Inventory()[objHash].payload)) + except KeyError: + connection.antiIntersectionDelay() + logger.warning('%s asked for an object with a getdata which is not in either our memory inventory or our SQL inventory. We probably cleaned it out after advertising it but before they got around to asking for it.' % (connection.destination,)) + + def stopThread(self): + super(ReceiveQueueThread, self).stopThread() diff --git a/src/network/socks4a.py b/src/network/socks4a.py index 02c8d4af..4b6b64fa 100644 --- a/src/network/socks4a.py +++ b/src/network/socks4a.py @@ -59,28 +59,28 @@ class Socks4aConnection(Socks4a): def state_auth_done(self): # Now we can request the actual connection rmtrslv = False - self.append_write_buf(struct.pack('>BBH', 0x04, 0x01, self.destination[1])) + self.writeQueue.put(struct.pack('>BBH', 0x04, 0x01, self.destination[1])) # If the given destination address is an IP address, we'll # use the IPv4 address request even if remote resolving was specified. try: self.ipaddr = socket.inet_aton(self.destination[0]) - self.append_write_buf(self.ipaddr) + self.writeQueue.put(self.ipaddr) except socket.error: # Well it's not an IP number, so it's probably a DNS name. if Proxy._remote_dns: # Resolve remotely rmtrslv = True self.ipaddr = None - self.append_write_buf(struct.pack("BBBB", 0x00, 0x00, 0x00, 0x01)) + self.writeQueue.put(struct.pack("BBBB", 0x00, 0x00, 0x00, 0x01)) else: # Resolve locally self.ipaddr = socket.inet_aton(socket.gethostbyname(self.destination[0])) - self.append_write_buf(self.ipaddr) + self.writeQueue.put(self.ipaddr) if self._auth: - self.append_write_buf(self._auth[0]) - self.append_write_buf(chr(0x00).encode()) + self.writeQueue.put(self._auth[0]) + self.writeQueue.put(chr(0x00).encode()) if rmtrslv: - self.append_write_buf(self.destination[0] + chr(0x00).encode()) + self.writeQueue.put(self.destination[0] + chr(0x00).encode()) self.set_state("pre_connect", 0) @@ -92,12 +92,12 @@ class Socks4aResolver(Socks4a): def state_auth_done(self): # Now we can request the actual connection - self.append_write_buf(struct.pack('>BBH', 0x04, 0xF0, self.destination[1])) - self.append_write_buf(struct.pack("BBBB", 0x00, 0x00, 0x00, 0x01)) + self.writeQueue.put(struct.pack('>BBH', 0x04, 0xF0, self.destination[1])) + self.writeQueue.put(struct.pack("BBBB", 0x00, 0x00, 0x00, 0x01)) if self._auth: - self.append_write_buf(self._auth[0]) - self.append_write_buf(chr(0x00).encode()) - self.append_write_buf(self.host + chr(0x00).encode()) + self.writeQueue.put(self._auth[0]) + self.writeQueue.put(chr(0x00).encode()) + self.writeQueue.put(self.host + chr(0x00).encode()) self.set_state("pre_connect", 0) def resolved(self): diff --git a/src/network/socks5.py b/src/network/socks5.py index 5ba6f3e3..0d1717d4 100644 --- a/src/network/socks5.py +++ b/src/network/socks5.py @@ -17,9 +17,9 @@ class Socks5(Proxy): def state_init(self): if self._auth: - self.append_write_buf(struct.pack('BBBB', 0x05, 0x02, 0x00, 0x02)) + self.writeQueue.put(struct.pack('BBBB', 0x05, 0x02, 0x00, 0x02)) else: - self.append_write_buf(struct.pack('BBB', 0x05, 0x01, 0x00)) + self.writeQueue.put(struct.pack('BBB', 0x05, 0x01, 0x00)) self.set_state("auth_1", 0) def state_auth_1(self): @@ -35,7 +35,7 @@ class Socks5(Proxy): self.set_state("auth_done", 2) elif ret[1] == 2: # username/password - self.append_write_buf(struct.pack('BB', 1, len(self._auth[0])) + \ + self.writeQueue.put(struct.pack('BB', 1, len(self._auth[0])) + \ self._auth[0] + struct.pack('B', len(self._auth[1])) + \ self._auth[1]) self.set_state("auth_1", 2) @@ -130,23 +130,23 @@ class Socks5Connection(Socks5): def state_auth_done(self): # Now we can request the actual connection - self.append_write_buf(struct.pack('BBB', 0x05, 0x01, 0x00)) + self.writeQueue.put(struct.pack('BBB', 0x05, 0x01, 0x00)) # If the given destination address is an IP address, we'll # use the IPv4 address request even if remote resolving was specified. try: self.ipaddr = socket.inet_aton(self.destination[0]) - self.append_write_buf(chr(0x01).encode() + self.ipaddr) + self.writeQueue.put(chr(0x01).encode() + self.ipaddr) except socket.error: # Well it's not an IP number, so it's probably a DNS name. if Proxy._remote_dns: # Resolve remotely self.ipaddr = None - self.append_write_buf(chr(0x03).encode() + chr(len(self.destination[0])).encode() + self.destination[0]) + self.writeQueue.put(chr(0x03).encode() + chr(len(self.destination[0])).encode() + self.destination[0]) else: # Resolve locally self.ipaddr = socket.inet_aton(socket.gethostbyname(self.destination[0])) - self.append_write_buf(chr(0x01).encode() + self.ipaddr) - self.append_write_buf(struct.pack(">H", self.destination[1])) + self.writeQueue.put(chr(0x01).encode() + self.ipaddr) + self.writeQueue.put(struct.pack(">H", self.destination[1])) self.set_state("pre_connect", 0) @@ -158,9 +158,9 @@ class Socks5Resolver(Socks5): def state_auth_done(self): # Now we can request the actual connection - self.append_write_buf(struct.pack('BBB', 0x05, 0xF0, 0x00)) - self.append_write_buf(chr(0x03).encode() + chr(len(self.host)).encode() + str(self.host)) - self.append_write_buf(struct.pack(">H", self.port)) + self.writeQueue.put(struct.pack('BBB', 0x05, 0xF0, 0x00)) + self.writeQueue.put(chr(0x03).encode() + chr(len(self.host)).encode() + str(self.host)) + self.writeQueue.put(struct.pack(">H", self.port)) self.set_state("pre_connect", 0) def resolved(self): diff --git a/src/protocol.py b/src/protocol.py index b7847e8f..83ecb7bd 100644 --- a/src/protocol.py +++ b/src/protocol.py @@ -67,7 +67,7 @@ def isBitSetWithinBitfield(fourByteString, n): x, = unpack('>L', fourByteString) return x & 2**n != 0 -# data handling +# ip addresses def encodeHost(host): if host.find('.onion') > -1: @@ -86,6 +86,49 @@ def networkType(host): else: return 'IPv6' +def checkIPAddress(host): + if host[0:12] == '\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xFF\xFF': + hostStandardFormat = socket.inet_ntop(socket.AF_INET, host[12:]) + return checkIPv4Address(host[12:], hostStandardFormat) + elif host[0:6] == '\xfd\x87\xd8\x7e\xeb\x43': + # Onion, based on BMD/bitcoind + hostStandardFormat = base64.b32encode(host[6:]).lower() + ".onion" + return hostStandardFormat + else: + hostStandardFormat = socket.inet_ntop(socket.AF_INET6, host) + if hostStandardFormat == "": + # This can happen on Windows systems which are not 64-bit compatible + # so let us drop the IPv6 address. + return False + return checkIPv6Address(host, hostStandardFormat) + +def checkIPv4Address(host, hostStandardFormat): + if host[0] == '\x7F': # 127/8 + logger.debug('Ignoring IP address in loopback range: ' + hostStandardFormat) + return False + if host[0] == '\x0A': # 10/8 + logger.debug('Ignoring IP address in private range: ' + hostStandardFormat) + return False + if host[0:2] == '\xC0\xA8': # 192.168/16 + logger.debug('Ignoring IP address in private range: ' + hostStandardFormat) + return False + if host[0:2] >= '\xAC\x10' and host[0:2] < '\xAC\x20': # 172.16/12 + logger.debug('Ignoring IP address in private range:' + hostStandardFormat) + return False + return hostStandardFormat + +def checkIPv6Address(host, hostStandardFormat): + if host == ('\x00' * 15) + '\x01': + logger.debug('Ignoring loopback address: ' + hostStandardFormat) + return False + if host[0] == '\xFE' and (ord(host[1]) & 0xc0) == 0x80: + logger.debug ('Ignoring local address: ' + hostStandardFormat) + return False + if (ord(host[0]) & 0xfe) == 0xfc: + logger.debug ('Ignoring unique local address: ' + hostStandardFormat) + return False + return hostStandardFormat + # checks def haveSSL(server = False): diff --git a/src/state.py b/src/state.py index c7b79e07..72852f3e 100644 --- a/src/state.py +++ b/src/state.py @@ -21,6 +21,8 @@ curses = False sqlReady = False # set to true by sqlTread when ready for processing +maximumNumberOfHalfOpenConnections = 0 + # If the trustedpeer option is specified in keys.dat then this will # contain a Peer which will be connected to instead of using the # addresses advertised by other peers. The client will only connect to From b37a05fd0affb67f03aaa2105d5d2ac72ff93dc9 Mon Sep 17 00:00:00 2001 From: Peter Surda Date: Sat, 27 May 2017 07:48:29 +0200 Subject: [PATCH 055/407] Allow encoding 3 in broadcast API --- src/api.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/api.py b/src/api.py index 82a56d40..24c0fc12 100644 --- a/src/api.py +++ b/src/api.py @@ -714,8 +714,8 @@ class MySimpleXMLRPCRequestHandler(SimpleXMLRPCRequestHandler): TTL = 4*24*60*60 elif len(params) == 5: fromAddress, subject, message, encodingType, TTL = params - if encodingType != 2: - raise APIError(6, 'The encoding type must be 2 because that is the only one this program currently supports.') + if encodingType not in [2, 3]: + raise APIError(6, 'The encoding type must be 2 or 3.') subject = self._decode(subject, "base64") message = self._decode(message, "base64") if len(subject + message) > (2 ** 18 - 500): From d699a28e4900f9670e0d8cf631ae0aaf44d7c914 Mon Sep 17 00:00:00 2001 From: Peter Surda Date: Sat, 27 May 2017 19:00:19 +0200 Subject: [PATCH 056/407] Add variables to errno and socket - to make sure they work cross platform without having to do complicated tests --- src/bitmessagemain.py | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/src/bitmessagemain.py b/src/bitmessagemain.py index c05d002a..8f39deb9 100755 --- a/src/bitmessagemain.py +++ b/src/bitmessagemain.py @@ -22,6 +22,7 @@ depends.check_dependencies() import signal # Used to capture a Ctrl-C keypress so that Bitmessage can shutdown gracefully. # The next 3 are used for the API from singleinstance import singleinstance +import errno import socket import ctypes from struct import pack @@ -92,7 +93,16 @@ def connectToStream(streamNumber): a.setup(streamNumber, selfInitiatedConnections) a.start() -def _fixWinsock(): +def _fixSocket(): + if sys.platform.startswith('linux'): + socket.SO_BINDTODEVICE = 25 + + if not sys.platform.startswith('win'): + errno.WSAEWOULDBLOCK = errno.EWOULDBLOCK + errno.WSAENETUNREACH = errno.ENETUNREACH + errno.WSAECONNREFUSED = errno.ECONNREFUSED + errno.WSAEHOSTUNREACH = errno.EHOSTUNREACH + if not sys.platform.startswith('win'): return @@ -177,7 +187,7 @@ if shared.useVeryEasyProofOfWorkForTesting: class Main: def start(self, daemon=False): - _fixWinsock() + _fixSocket() shared.daemon = daemon From 7b9b7504adf36da5b372c8c93131b2635068cb55 Mon Sep 17 00:00:00 2001 From: Peter Surda Date: Sat, 27 May 2017 19:01:14 +0200 Subject: [PATCH 057/407] Don't clean right on startup --- src/class_singleCleaner.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/class_singleCleaner.py b/src/class_singleCleaner.py index 1ed7a5e4..6c0a62f6 100644 --- a/src/class_singleCleaner.py +++ b/src/class_singleCleaner.py @@ -49,6 +49,10 @@ class singleCleaner(threading.Thread, StoppableThread): # Either the user hasn't set stopresendingafterxdays and stopresendingafterxmonths yet or the options are missing from the config file. shared.maximumLengthOfTimeToBotherResendingMessages = float('inf') + # initial wait + if state.shutdown == 0: + self.stop.wait(300) + while state.shutdown == 0: queues.UISignalQueue.put(( 'updateStatusBar', 'Doing housekeeping (Flushing inventory in memory to disk...)')) From 1d87c635043c7cab027ddc346b2228e695dbd5ae Mon Sep 17 00:00:00 2001 From: Peter Surda Date: Sat, 27 May 2017 19:02:05 +0200 Subject: [PATCH 058/407] Traceback on Ctrl-C - Ctrl-C will print a traceback of all threads instead of complaining --- src/helper_generic.py | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/src/helper_generic.py b/src/helper_generic.py index f2a293cd..26d65dcb 100644 --- a/src/helper_generic.py +++ b/src/helper_generic.py @@ -4,6 +4,7 @@ import sys from binascii import hexlify, unhexlify from multiprocessing import current_process from threading import current_thread, enumerate +import traceback from bmconfigparser import BMConfigParser from debug import logger @@ -29,10 +30,20 @@ def convertIntToString(n): else: return unhexlify('0' + a[2:]) - def convertStringToInt(s): return int(hexlify(s), 16) +def allThreadTraceback(frame): + id2name = dict([(th.ident, th.name) for th in enumerate()]) + code = [] + for threadId, stack in sys._current_frames().items(): + code.append("\n# Thread: %s(%d)" % (id2name.get(threadId,""), threadId)) + for filename, lineno, name, line in traceback.extract_stack(stack): + code.append('File: "%s", line %d, in %s' % (filename, lineno, name)) + if line: + code.append(" %s" % (line.strip())) + print "\n".join(code) + def signal_handler(signal, frame): logger.error("Got signal %i in %s/%s", signal, current_process().name, current_thread().name) if current_process().name == "RegExParser": @@ -46,6 +57,7 @@ def signal_handler(signal, frame): if BMConfigParser().safeGetBoolean('bitmessagesettings', 'daemon'): shutdown.doCleanShutdown() else: + allThreadTraceback(frame) print 'Unfortunately you cannot use Ctrl+C when running the UI because the UI captures the signal.' def isHostInPrivateIPRange(host): From 36b5e2c04f31c8ded967b511c0ae4bd801f3f807 Mon Sep 17 00:00:00 2001 From: Peter Surda Date: Sat, 27 May 2017 19:03:27 +0200 Subject: [PATCH 059/407] Inventory storage abstraction - can have multiple storage types for inventory - sqlite is the old one, filesystem is a new available --- src/inventory.py | 94 +++++--------------- src/storage/__init__.py | 0 src/storage/filesystem.py | 175 ++++++++++++++++++++++++++++++++++++++ src/storage/sqlite.py | 81 ++++++++++++++++++ src/storage/storage.py | 51 +++++++++++ 5 files changed, 329 insertions(+), 72 deletions(-) create mode 100644 src/storage/__init__.py create mode 100644 src/storage/filesystem.py create mode 100644 src/storage/sqlite.py create mode 100644 src/storage/storage.py diff --git a/src/inventory.py b/src/inventory.py index a796968a..b676415b 100644 --- a/src/inventory.py +++ b/src/inventory.py @@ -1,87 +1,37 @@ import collections +from importlib import import_module from threading import current_thread, enumerate as threadingEnumerate, RLock import Queue import time +import sys +from bmconfigparser import BMConfigParser from helper_sql import * from singleton import Singleton +# TODO make this dynamic, and watch out for frozen, like with messagetypes +import storage.sqlite +import storage.filesystem @Singleton -class Inventory(collections.MutableMapping): +class Inventory(): def __init__(self): - super(self.__class__, self).__init__() - self._inventory = {} #of objects (like msg payloads and pubkey payloads) Does not include protocol headers (the first 24 bytes of each packet). - self.numberOfInventoryLookupsPerformed = 0 - self._streams = collections.defaultdict(set) # key = streamNumer, value = a set which holds the inventory object hashes that we are aware of. This is used whenever we receive an inv message from a peer to check to see what items are new to us. We don't delete things out of it; instead, the singleCleaner thread clears and refills it every couple hours. - self.lock = RLock() # Guarantees that two receiveDataThreads don't receive and process the same message concurrently (probably sent by a malicious individual) - self.InventoryItem = collections.namedtuple('InventoryItem', 'type stream payload expires tag') + #super(self.__class__, self).__init__() + self._moduleName = BMConfigParser().safeGet("inventory", "storage") + #import_module("." + self._moduleName, "storage") + #import_module("storage." + self._moduleName) + self._className = "storage." + self._moduleName + "." + self._moduleName.title() + "Inventory" + self._inventoryClass = eval(self._className) + self._realInventory = self._inventoryClass() - def __contains__(self, hash): - with self.lock: - self.numberOfInventoryLookupsPerformed += 1 - if hash in self._inventory: - return True - return bool(sqlQuery('SELECT 1 FROM inventory WHERE hash=?', hash)) - - def __getitem__(self, hash): - with self.lock: - if hash in self._inventory: - return self._inventory[hash] - rows = sqlQuery('SELECT objecttype, streamnumber, payload, expirestime, tag FROM inventory WHERE hash=?', hash) - if not rows: - raise KeyError(hash) - return self.InventoryItem(*rows[0]) - - def __setitem__(self, hash, value): - with self.lock: - value = self.InventoryItem(*value) - self._inventory[hash] = value - self._streams[value.stream].add(hash) - - def __delitem__(self, hash): - raise NotImplementedError - - def __iter__(self): - with self.lock: - hashes = self._inventory.keys()[:] - hashes += (x for x, in sqlQuery('SELECT hash FROM inventory')) - return hashes.__iter__() - - def __len__(self): - with self.lock: - return len(self._inventory) + sqlQuery('SELECT count(*) FROM inventory')[0][0] - - def by_type_and_tag(self, type, tag): - with self.lock: - values = [value for value in self._inventory.values() if value.type == type and value.tag == tag] - values += (self.InventoryItem(*value) for value in sqlQuery('SELECT objecttype, streamnumber, payload, expirestime, tag FROM inventory WHERE objecttype=? AND tag=?', type, tag)) - return values - - def hashes_by_stream(self, stream): - with self.lock: - return self._streams[stream] - - def unexpired_hashes_by_stream(self, stream): - with self.lock: - t = int(time.time()) - hashes = [x for x, value in self._inventory.items() if value.stream == stream and value.expires > t] - hashes += (payload for payload, in sqlQuery('SELECT hash FROM inventory WHERE streamnumber=? AND expirestime>?', stream, t)) - return hashes - - def flush(self): - with self.lock: # If you use both the inventoryLock and the sqlLock, always use the inventoryLock OUTSIDE of the sqlLock. - with SqlBulkExecute() as sql: - for objectHash, value in self._inventory.items(): - sql.execute('INSERT INTO inventory VALUES (?, ?, ?, ?, ?, ?)', objectHash, *value) - self._inventory.clear() - - def clean(self): - with self.lock: - sqlExecute('DELETE FROM inventory WHERE expirestime t] + except KeyError: + return [] + + def flush(self): + self._load() + + def clean(self): + minTime = int(time.time()) - (60 * 60 * 30) + deletes = [] + for stream, streamDict in self._inventory.items(): + for hashId, item in streamDict.items(): + if item.expires < minTime: + deletes.append(hashId) + for hashId in deletes: + del self[hashId] diff --git a/src/storage/sqlite.py b/src/storage/sqlite.py new file mode 100644 index 00000000..38e9c0a2 --- /dev/null +++ b/src/storage/sqlite.py @@ -0,0 +1,81 @@ +import collections +from threading import current_thread, enumerate as threadingEnumerate, RLock +import Queue +import time + +from helper_sql import * +from storage import InventoryStorage, InventoryItem + +class SqliteInventory(InventoryStorage): + def __init__(self): + super(self.__class__, self).__init__() + self._inventory = {} #of objects (like msg payloads and pubkey payloads) Does not include protocol headers (the first 24 bytes of each packet). + self._streams = collections.defaultdict(set) # key = streamNumer, value = a set which holds the inventory object hashes that we are aware of. This is used whenever we receive an inv message from a peer to check to see what items are new to us. We don't delete things out of it; instead, the singleCleaner thread clears and refills it every couple hours. + self.lock = RLock() # Guarantees that two receiveDataThreads don't receive and process the same message concurrently (probably sent by a malicious individual) + + def __contains__(self, hash): + with self.lock: + self.numberOfInventoryLookupsPerformed += 1 + if hash in self._inventory: + return True + return bool(sqlQuery('SELECT 1 FROM inventory WHERE hash=?', hash)) + + def __getitem__(self, hash): + with self.lock: + if hash in self._inventory: + return self._inventory[hash] + rows = sqlQuery('SELECT objecttype, streamnumber, payload, expirestime, tag FROM inventory WHERE hash=?', hash) + if not rows: + raise KeyError(hash) + return InventoryItem(*rows[0]) + + def __setitem__(self, hash, value): + with self.lock: + value = InventoryItem(*value) + self._inventory[hash] = value + self._streams[value.stream].add(hash) + + def __delitem__(self, hash): + raise NotImplementedError + + def __iter__(self): + with self.lock: + hashes = self._inventory.keys()[:] + hashes += (x for x, in sqlQuery('SELECT hash FROM inventory')) + return hashes.__iter__() + + def __len__(self): + with self.lock: + return len(self._inventory) + sqlQuery('SELECT count(*) FROM inventory')[0][0] + + def by_type_and_tag(self, type, tag): + with self.lock: + values = [value for value in self._inventory.values() if value.type == type and value.tag == tag] + values += (InventoryItem(*value) for value in sqlQuery('SELECT objecttype, streamnumber, payload, expirestime, tag FROM inventory WHERE objecttype=? AND tag=?', type, tag)) + return values + + def hashes_by_stream(self, stream): + with self.lock: + return self._streams[stream] + + def unexpired_hashes_by_stream(self, stream): + with self.lock: + t = int(time.time()) + hashes = [x for x, value in self._inventory.items() if value.stream == stream and value.expires > t] + hashes += (payload for payload, in sqlQuery('SELECT hash FROM inventory WHERE streamnumber=? AND expirestime>?', stream, t)) + return hashes + + def flush(self): + with self.lock: # If you use both the inventoryLock and the sqlLock, always use the inventoryLock OUTSIDE of the sqlLock. + with SqlBulkExecute() as sql: + for objectHash, value in self._inventory.items(): + sql.execute('INSERT INTO inventory VALUES (?, ?, ?, ?, ?, ?)', objectHash, *value) + self._inventory.clear() + + def clean(self): + with self.lock: + sqlExecute('DELETE FROM inventory WHERE expirestime Date: Sat, 27 May 2017 19:09:21 +0200 Subject: [PATCH 060/407] Asyncore update - bugfixes - UDP socket for local peer discovery - new function assembleAddr to unify creating address command - open port checker functionality (inactive) - sendBigInv is done in a thread separate from the network IO thread --- src/bitmessagemain.py | 13 +- src/bmconfigparser.py | 6 +- src/network/advanceddispatcher.py | 16 +- src/network/announcethread.py | 39 ++ src/network/bmproto.py | 375 +++++-------------- src/network/connectionchooser.py | 7 +- src/network/connectionpool.py | 63 +++- src/network/{bmqueues.py => objectracker.py} | 25 +- src/network/receivequeuethread.py | 43 ++- src/network/tcp.py | 236 ++++++++++++ src/network/tls.py | 8 +- src/network/udp.py | 198 ++++++++++ src/protocol.py | 24 +- src/queues.py | 1 + 14 files changed, 721 insertions(+), 333 deletions(-) create mode 100644 src/network/announcethread.py rename src/network/{bmqueues.py => objectracker.py} (74%) create mode 100644 src/network/tcp.py create mode 100644 src/network/udp.py diff --git a/src/bitmessagemain.py b/src/bitmessagemain.py index 8f39deb9..d566852c 100755 --- a/src/bitmessagemain.py +++ b/src/bitmessagemain.py @@ -51,9 +51,13 @@ from class_smtpDeliver import smtpDeliver from class_smtpServer import smtpServer from bmconfigparser import BMConfigParser +from inventory import Inventory + from network.connectionpool import BMConnectionPool from network.networkthread import BMNetworkThread from network.receivequeuethread import ReceiveQueueThread +from network.announcethread import AnnounceThread +#from network.downloadthread import DownloadThread # Helper Functions import helper_bootstrap @@ -221,6 +225,8 @@ class Main: sqlLookup.daemon = False # DON'T close the main program even if there are threads left. The closeEvent should command this thread to exit gracefully. sqlLookup.start() + Inventory() # init + # SMTP delivery thread if daemon and BMConfigParser().safeGet("bitmessagesettings", "smtpdeliver", '') != '': smtpDeliveryThread = smtpDeliver() @@ -261,11 +267,14 @@ class Main: if BMConfigParser().safeGetBoolean("network", "asyncore"): asyncoreThread = BMNetworkThread() - asyncoreThread.daemon = False + asyncoreThread.daemon = True asyncoreThread.start() receiveQueueThread = ReceiveQueueThread() - receiveQueueThread.daemon = False + receiveQueueThread.daemon = True receiveQueueThread.start() + announceThread = AnnounceThread() + announceThread.daemon = True + announceThread.start() connectToStream(1) diff --git a/src/bmconfigparser.py b/src/bmconfigparser.py index 4e66c703..8c4d8b3c 100644 --- a/src/bmconfigparser.py +++ b/src/bmconfigparser.py @@ -16,7 +16,11 @@ BMConfigDefaults = { "maxuploadrate": 0, }, "network": { - "asyncore": False + "asyncore": False, + "bind": None, + }, + "inventory": { + "storage": "sqlite", }, "zlib": { 'maxsize': 1048576 diff --git a/src/network/advanceddispatcher.py b/src/network/advanceddispatcher.py index fb28f3d4..938eb11d 100644 --- a/src/network/advanceddispatcher.py +++ b/src/network/advanceddispatcher.py @@ -36,7 +36,10 @@ class AdvancedDispatcher(asyncore.dispatcher): def process(self): if self.state not in ["init", "tls_handshake"] and len(self.read_buf) == 0: return - while True: + if not self.connected: + return + maxLoop = 20 + while maxLoop > 0: try: # print "Trying to handle state \"%s\"" % (self.state) if getattr(self, "state_" + str(self.state))() is False: @@ -44,6 +47,7 @@ class AdvancedDispatcher(asyncore.dispatcher): except AttributeError: # missing state raise + maxLoop -= 1 def set_state(self, state, length=0): self.slice_read_buf(length) @@ -96,4 +100,14 @@ class AdvancedDispatcher(asyncore.dispatcher): self.read_buf = b"" self.write_buf = b"" self.state = "shutdown" + while True: + try: + self.writeQueue.get(False) + except Queue.Empty: + break + while True: + try: + self.receiveQueue.get(False) + except Queue.Empty: + break asyncore.dispatcher.close(self) diff --git a/src/network/announcethread.py b/src/network/announcethread.py new file mode 100644 index 00000000..0ba93d7a --- /dev/null +++ b/src/network/announcethread.py @@ -0,0 +1,39 @@ +import Queue +import threading +import time + +from bmconfigparser import BMConfigParser +from debug import logger +from helper_threading import StoppableThread +from network.bmproto import BMProto +from network.connectionpool import BMConnectionPool +from network.udp import UDPSocket +import protocol +import state + +class AnnounceThread(threading.Thread, StoppableThread): + def __init__(self): + threading.Thread.__init__(self, name="AnnounceThread") + self.initStop() + self.name = "AnnounceThread" + BMConnectionPool() + logger.error("init announce thread") + + def run(self): + lastSelfAnnounced = 0 + while not self._stopped: + processed = 0 + if lastSelfAnnounced < time.time() - UDPSocket.announceInterval: + self.announceSelf() + lastSelfAnnounced = time.time() + if processed == 0: + self.stop.wait(10) + + def announceSelf(self): + for connection in BMConnectionPool().udpSockets.values(): + for stream in state.streamsInWhichIAmParticipating: + addr = (stream, state.Peer('127.0.0.1', BMConfigParser().safeGetInt("bitmessagesettings", "port")), time.time()) + connection.writeQueue.put(BMProto.assembleAddr([addr])) + + def stopThread(self): + super(AnnounceThread, self).stopThread() diff --git a/src/network/bmproto.py b/src/network/bmproto.py index 88a8f794..6e1c3a18 100644 --- a/src/network/bmproto.py +++ b/src/network/bmproto.py @@ -19,12 +19,9 @@ import network.connectionpool from network.downloadqueue import DownloadQueue from network.node import Node import network.asyncore_pollchoose as asyncore +from network.objectracker import ObjectTracker from network.proxy import Proxy, ProxyError, GeneralProxyError -from network.bmqueues import BMQueues -from network.socks5 import Socks5Connection, Socks5Resolver, Socks5AuthError, Socks5Error -from network.socks4a import Socks4aConnection, Socks4aResolver, Socks4aError from network.uploadqueue import UploadQueue, UploadElem, AddrUploadQueue, ObjUploadQueue -from network.tls import TLSDispatcher import addresses from bmconfigparser import BMConfigParser @@ -42,7 +39,7 @@ class BMProtoInsufficientDataError(BMProtoError): pass class BMProtoExcessiveDataError(BMProtoError): pass -class BMConnection(TLSDispatcher, BMQueues): +class BMProto(AdvancedDispatcher, ObjectTracker): # ~1.6 MB which is the maximum possible size of an inv message. maxMessageSize = 1600100 # 2**18 = 256kB is the maximum size of an object payload @@ -51,36 +48,40 @@ class BMConnection(TLSDispatcher, BMQueues): maxAddrCount = 1000 # protocol specification says max 50000 objects in one inv command maxObjectCount = 50000 + # address is online if online less than this many seconds ago + addressAlive = 10800 + # maximum time offset + maxTimeOffset = 3600 - def __init__(self, address=None, sock=None): - AdvancedDispatcher.__init__(self, sock) - self.verackReceived = False - self.verackSent = False - self.lastTx = time.time() - self.streams = [0] - self.fullyEstablished = False - self.connectedAt = 0 - self.skipUntil = 0 - if address is None and sock is not None: - self.destination = state.Peer(sock.getpeername()[0], sock.getpeername()[1]) - self.isOutbound = False - TLSDispatcher.__init__(self, sock, server_side=True) - self.connectedAt = time.time() - print "received connection in background from %s:%i" % (self.destination.host, self.destination.port) - else: - self.destination = address - self.isOutbound = True - if ":" in address.host: - self.create_socket(socket.AF_INET6, socket.SOCK_STREAM) - else: - self.create_socket(socket.AF_INET, socket.SOCK_STREAM) - self.socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) - TLSDispatcher.__init__(self, sock, server_side=False) - self.connect(self.destination) - print "connecting in background to %s:%i" % (self.destination.host, self.destination.port) - shared.connectedHostsList[self.destination] = 0 - BMQueues.__init__(self) - UISignalQueue.put(('updateNetworkStatusTab', 'no data')) +# def __init__(self, address=None, sock=None): +# AdvancedDispatcher.__init__(self, sock) +# self.verackReceived = False +# self.verackSent = False +# self.lastTx = time.time() +# self.streams = [0] +# self.fullyEstablished = False +# self.connectedAt = 0 +# self.skipUntil = 0 +# if address is None and sock is not None: +# self.destination = state.Peer(sock.getpeername()[0], sock.getpeername()[1]) +# self.isOutbound = False +# TLSDispatcher.__init__(self, sock, server_side=True) +# self.connectedAt = time.time() +# #print "received connection in background from %s:%i" % (self.destination.host, self.destination.port) +# else: +# self.destination = address +# self.isOutbound = True +# if ":" in address.host: +# self.create_socket(socket.AF_INET6, socket.SOCK_STREAM) +# else: +# self.create_socket(socket.AF_INET, socket.SOCK_STREAM) +# self.socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) +# TLSDispatcher.__init__(self, sock, server_side=False) +# self.connect(self.destination) +# #print "connecting in background to %s:%i" % (self.destination.host, self.destination.port) +# shared.connectedHostsList[self.destination] = 0 +# ObjectTracker.__init__(self) +# UISignalQueue.put(('updateNetworkStatusTab', 'no data')) def bm_proto_reset(self): self.magic = None @@ -92,37 +93,6 @@ class BMConnection(TLSDispatcher, BMQueues): self.payloadOffset = 0 self.object = None - def state_init(self): - self.bm_proto_reset() - if self.isOutbound: - self.writeQueue.put(protocol.assembleVersionMessage(self.destination.host, self.destination.port, network.connectionpool.BMConnectionPool().streams, False)) - print "%s:%i: Sending version" % (self.destination.host, self.destination.port) - self.set_state("bm_header") - return True - - def antiIntersectionDelay(self, initial = False): - # estimated time for a small object to propagate across the whole network - delay = math.ceil(math.log(max(len(knownnodes.knownNodes[x]) for x in knownnodes.knownNodes) + 2, 20)) * (0.2 + UploadQueue.queueCount/2) - # take the stream with maximum amount of nodes - # +2 is to avoid problems with log(0) and log(1) - # 20 is avg connected nodes count - # 0.2 is avg message transmission time - if delay > 0: - if initial: - self.skipUntil = self.connectedAt + delay - if self.skipUntil > time.time(): - logger.debug("Skipping processing for %.2fs", self.skipUntil - time.time()) - else: - logger.debug("Skipping processing due to missing object for %.2fs", self.skipUntil - time.time()) - self.skipUntil = time.time() + delay - - def set_connection_fully_established(self): - UISignalQueue.put(('updateNetworkStatusTab', 'no data')) - self.antiIntersectionDelay(True) - self.fullyEstablished = True - self.sendAddr() - self.sendBigInv() - def state_bm_header(self): #print "%s:%i: header" % (self.destination.host, self.destination.port) if len(self.read_buf) < protocol.Header.size: @@ -137,7 +107,7 @@ class BMConnection(TLSDispatcher, BMQueues): print "Bad magic" self.close() return False - if self.payloadLength > BMConnection.maxMessageSize: + if self.payloadLength > BMProto.maxMessageSize: self.invalid = True self.set_state("bm_command", protocol.Header.size) return True @@ -309,28 +279,18 @@ class BMConnection(TLSDispatcher, BMQueues): def bm_command_inv(self): items = self.decode_payload_content("L32s") - if len(items) >= BMConnection.maxObjectCount: + if len(items) >= BMProto.maxObjectCount: logger.error("Too many items in inv message!") raise BMProtoExcessiveDataError() else: pass - #print "items in inv: %i" % (len(items)) - startTime = time.time() - #advertisedSet = set() for i in items: - #advertisedSet.add(i) - self.handleReceivedObj(i) - #objectsNewToMe = advertisedSet - #for stream in self.streams: - #objectsNewToMe -= Inventory().hashes_by_stream(stream) - logger.info('inv message lists %i objects. Of those %i are new to me. It took %f seconds to figure that out.', len(items), len(self.objectsNewToMe), time.time()-startTime) + self.receiveQueue.put(("inv", i)) + self.handleReceivedInventory(i) payload = addresses.encodeVarint(len(self.objectsNewToMe)) + ''.join(self.objectsNewToMe.keys()) self.writeQueue.put(protocol.CreatePacket('getdata', payload)) - -# for i in random.sample(self.objectsNewToMe, len(self.objectsNewToMe)): -# DownloadQueue().put(i) return True def bm_command_object(self): @@ -338,7 +298,7 @@ class BMConnection(TLSDispatcher, BMQueues): nonce, expiresTime, objectType, version, streamNumber = self.decode_payload_content("QQIvv") self.object = BMObject(nonce, expiresTime, objectType, version, streamNumber, self.payload) - if len(self.payload) - self.payloadOffset > BMConnection.maxObjectPayloadSize: + if len(self.payload) - self.payloadOffset > BMProto.maxObjectPayloadSize: logger.info('The payload length of this object is too large (%s bytes). Ignoring it.' % len(self.payload) - self.payloadOffset) raise BMProtoExcessiveDataError() @@ -368,20 +328,23 @@ class BMConnection(TLSDispatcher, BMQueues): #broadcastToSendDataQueues((streamNumber, 'advertiseobject', inventoryHash)) return True + def _decode_addr(self): + return self.decode_payload_content("lQIQ16sH") + def bm_command_addr(self): - addresses = self.decode_payload_content("lQIQ16sH") - import pprint + addresses = self._decode_addr() for i in addresses: seenTime, stream, services, ip, port = i decodedIP = protocol.checkIPAddress(ip) if stream not in state.streamsInWhichIAmParticipating: continue #print "maybe adding %s in stream %i to knownnodes (%i)" % (decodedIP, stream, len(knownnodes.knownNodes[stream])) - if decodedIP is not False and seenTime > time.time() - 10800: + if decodedIP is not False and seenTime > time.time() - BMProto.addressAlive: peer = state.Peer(decodedIP, port) if peer in knownnodes.knownNodes[stream] and knownnodes.knownNodes[stream][peer] > seenTime: continue knownnodes.knownNodes[stream][peer] = seenTime + AddrUploadQueue().put((stream, peer)) return True def bm_command_portcheck(self): @@ -411,14 +374,15 @@ class BMConnection(TLSDispatcher, BMQueues): def bm_command_version(self): #self.remoteProtocolVersion, self.services, self.timestamp, padding1, self.myExternalIP, padding2, self.remoteNodeIncomingPort = protocol.VersionPacket.unpack(self.payload[:protocol.VersionPacket.size]) self.remoteProtocolVersion, self.services, self.timestamp, self.sockNode, self.peerNode, self.nonce, self.userAgent, self.streams = self.decode_payload_content("IQQiiQlslv") + self.nonce = struct.pack('>Q', self.nonce) self.timeOffset = self.timestamp - int(time.time()) - print "remoteProtocolVersion: %i" % (self.remoteProtocolVersion) - print "services: %08X" % (self.services) - print "time offset: %i" % (self.timestamp - int(time.time())) - print "my external IP: %s" % (self.sockNode.host) - print "remote node incoming port: %i" % (self.peerNode.port) - print "user agent: %s" % (self.userAgent) - print "streams: [%s]" % (",".join(map(str,self.streams))) + #print "remoteProtocolVersion: %i" % (self.remoteProtocolVersion) + #print "services: %08X" % (self.services) + #print "time offset: %i" % (self.timestamp - int(time.time())) + #print "my external IP: %s" % (self.sockNode.host) + #print "remote node incoming port: %i" % (self.peerNode.port) + #print "user agent: %s" % (self.userAgent) + #print "streams: [%s]" % (",".join(map(str,self.streams))) if not self.peerValidityChecks(): # TODO ABORT return True @@ -446,20 +410,20 @@ class BMConnection(TLSDispatcher, BMQueues): self.writeQueue.put(protocol.assembleErrorMessage(fatal=2, errorText="Your is using an old protocol. Closing connection.")) logger.debug ('Closing connection to old protocol version %s, node: %s', - str(self.remoteProtocolVersion), str(self.peer)) + str(self.remoteProtocolVersion), str(self.destination)) return False - if self.timeOffset > 3600: + if self.timeOffset > BMProto.maxTimeOffset: self.writeQueue.put(protocol.assembleErrorMessage(fatal=2, errorText="Your time is too far in the future compared to mine. Closing connection.")) logger.info("%s's time is too far in the future (%s seconds). Closing connection to it.", - self.peer, self.timeOffset) + self.destination, self.timeOffset) shared.timeOffsetWrongCount += 1 return False - elif self.timeOffset < -3600: + elif self.timeOffset < -BMProto.maxTimeOffset: self.writeQueue.put(protocol.assembleErrorMessage(fatal=2, errorText="Your time is too far in the past compared to mine. Closing connection.")) logger.info("%s's time is too far in the past (timeOffset %s seconds). Closing connection to it.", - self.peer, self.timeOffset) + self.destination, self.timeOffset) shared.timeOffsetWrongCount += 1 return False else: @@ -468,7 +432,7 @@ class BMConnection(TLSDispatcher, BMQueues): self.writeQueue.put(protocol.assembleErrorMessage(fatal=2, errorText="We don't have shared stream interests. Closing connection.")) logger.debug ('Closed connection to %s because there is no overlapping interest in streams.', - str(self.peer)) + str(self.destination)) return False if self.destination in network.connectionpool.BMConnectionPool().inboundConnections: try: @@ -476,218 +440,63 @@ class BMConnection(TLSDispatcher, BMQueues): self.writeQueue.put(protocol.assembleErrorMessage(fatal=2, errorText="Too many connections from your IP. Closing connection.")) logger.debug ('Closed connection to %s because we are already connected to that IP.', - str(self.peer)) + str(self.destination)) return False except: pass + if self.nonce == protocol.eightBytesOfRandomDataUsedToDetectConnectionsToSelf: + self.writeQueue.put(protocol.assembleErrorMessage(fatal=2, + errorText="I'm connected to myself. Closing connection.")) + logger.debug ("Closed connection to %s because I'm connected to myself.", + str(self.destination)) + return True - def sendAddr(self): - def sendChunk(): - if addressCount == 0: - return - self.writeQueue.put(protocol.CreatePacket('addr', \ - addresses.encodeVarint(addressCount) + payload)) - - # We are going to share a maximum number of 1000 addrs (per overlapping - # stream) with our peer. 500 from overlapping streams, 250 from the - # left child stream, and 250 from the right child stream. - maxAddrCount = BMConfigParser().safeGetInt("bitmessagesettings", "maxaddrperstreamsend", 500) - - # init - addressCount = 0 - payload = b'' - - for stream in self.streams: - addrsInMyStream = {} - addrsInChildStreamLeft = {} - addrsInChildStreamRight = {} - - with knownnodes.knownNodesLock: - if len(knownnodes.knownNodes[stream]) > 0: - filtered = {k: v for k, v in knownnodes.knownNodes[stream].items() - if v > (int(time.time()) - shared.maximumAgeOfNodesThatIAdvertiseToOthers)} - elemCount = len(filtered) - if elemCount > maxAddrCount: - elemCount = maxAddrCount - # only if more recent than 3 hours - addrsInMyStream = random.sample(filtered.items(), elemCount) - # sent 250 only if the remote isn't interested in it - if len(knownnodes.knownNodes[stream * 2]) > 0 and stream not in self.streams: - filtered = {k: v for k, v in knownnodes.knownNodes[stream*2].items() - if v > (int(time.time()) - shared.maximumAgeOfNodesThatIAdvertiseToOthers)} - elemCount = len(filtered) - if elemCount > maxAddrCount / 2: - elemCount = int(maxAddrCount / 2) - addrsInChildStreamLeft = random.sample(filtered.items(), elemCount) - if len(knownnodes.knownNodes[(stream * 2) + 1]) > 0 and stream not in self.streams: - filtered = {k: v for k, v in knownnodes.knownNodes[stream*2+1].items() - if v > (int(time.time()) - shared.maximumAgeOfNodesThatIAdvertiseToOthers)} - elemCount = len(filtered) - if elemCount > maxAddrCount / 2: - elemCount = int(maxAddrCount / 2) - addrsInChildStreamRight = random.sample(filtered.items(), elemCount) - for (HOST, PORT), timeLastReceivedMessageFromThisNode in addrsInMyStream: - addressCount += 1 - payload += struct.pack( - '>Q', timeLastReceivedMessageFromThisNode) # 64-bit time - payload += struct.pack('>I', stream) - payload += struct.pack( - '>q', 1) # service bit flags offered by this node - payload += protocol.encodeHost(HOST) - payload += struct.pack('>H', PORT) # remote port - if addressCount >= BMConnection.maxAddrCount: - sendChunk() - payload = b'' - addressCount = 0 - for (HOST, PORT), timeLastReceivedMessageFromThisNode in addrsInChildStreamLeft: - addressCount += 1 - payload += struct.pack( - '>Q', timeLastReceivedMessageFromThisNode) # 64-bit time - payload += struct.pack('>I', stream * 2) - payload += struct.pack( - '>q', 1) # service bit flags offered by this node - payload += protocol.encodeHost(HOST) - payload += struct.pack('>H', PORT) # remote port - if addressCount >= BMConnection.maxAddrCount: - sendChunk() - payload = b'' - addressCount = 0 - for (HOST, PORT), timeLastReceivedMessageFromThisNode in addrsInChildStreamRight: - addressCount += 1 - payload += struct.pack( - '>Q', timeLastReceivedMessageFromThisNode) # 64-bit time - payload += struct.pack('>I', (stream * 2) + 1) - payload += struct.pack( - '>q', 1) # service bit flags offered by this node - payload += protocol.encodeHost(HOST) - payload += struct.pack('>H', PORT) # remote port - if addressCount >= BMConnection.maxAddrCount: - sendChunk() - payload = b'' - addressCount = 0 - - # flush - sendChunk() - - def sendBigInv(self): - def sendChunk(): - if objectCount == 0: - return - logger.debug('Sending huge inv message with %i objects to just this one peer', objectCount) - self.writeQueue.put(protocol.CreatePacket('inv', addresses.encodeVarint(objectCount) + payload)) - - # Select all hashes for objects in this stream. - bigInvList = {} - for stream in self.streams: - for hash in Inventory().unexpired_hashes_by_stream(stream): - bigInvList[hash] = 0 -# for hash in ObjUploadQueue().streamHashes(stream): -# try: -# del bigInvList[hash] -# except KeyError: -# pass - objectCount = 0 - payload = b'' - # Now let us start appending all of these hashes together. They will be - # sent out in a big inv message to our new peer. - for hash, storedValue in bigInvList.items(): - payload += hash - objectCount += 1 - if objectCount >= BMConnection.maxObjectCount: - self.sendChunk() - payload = b'' - objectCount = 0 - - # flush - sendChunk() + @staticmethod + def assembleAddr(peerList): + if type(peerList) is state.Peer: + peerList = (peerList) + # TODO handle max length, now it's done by upper layers + payload = addresses.encodeVarint(len(peerList)) + for address in peerList: + stream, peer, timestamp = address + payload += struct.pack( + '>Q', timestamp) # 64-bit time + payload += struct.pack('>I', stream) + payload += struct.pack( + '>q', 1) # service bit flags offered by this node + payload += protocol.encodeHost(peer.host) + payload += struct.pack('>H', peer.port) # remote port + return protocol.CreatePacket('addr', payload) def handle_connect_event(self): try: asyncore.dispatcher.handle_connect_event(self) self.connectedAt = time.time() except socket.error as e: - print "%s:%i: socket error: %s" % (self.destination.host, self.destination.port, str(e)) + #print "%s:%i: socket error: %s" % (self.destination.host, self.destination.port, str(e)) self.close() def handle_read_event(self): try: asyncore.dispatcher.handle_read_event(self) except socket.error as e: - print "%s:%i: socket error: %s" % (self.destination.host, self.destination.port, str(e)) + #print "%s:%i: socket error: %s" % (self.destination.host, self.destination.port, str(e)) self.close() def handle_write_event(self): try: asyncore.dispatcher.handle_write_event(self) except socket.error as e: - print "%s:%i: socket error: %s" % (self.destination.host, self.destination.port, str(e)) + #print "%s:%i: socket error: %s" % (self.destination.host, self.destination.port, str(e)) self.close() def close(self, reason=None): self.set_state("close") - if reason is None: - print "%s:%i: closing" % (self.destination.host, self.destination.port) - #traceback.print_stack() - else: - print "%s:%i: closing, %s" % (self.destination.host, self.destination.port, reason) +# if reason is None: +# print "%s:%i: closing" % (self.destination.host, self.destination.port) +# #traceback.print_stack() +# else: +# print "%s:%i: closing, %s" % (self.destination.host, self.destination.port, reason) network.connectionpool.BMConnectionPool().removeConnection(self) - asyncore.dispatcher.close(self) - - -class Socks5BMConnection(Socks5Connection, BMConnection): - def __init__(self, address): - Socks5Connection.__init__(self, address=address) - - def state_socks_handshake_done(self): - BMConnection.state_init(self) - return False - - -class Socks4aBMConnection(Socks4aConnection, BMConnection): - def __init__(self, address): - Socks4aConnection.__init__(self, address=address) - - def state_socks_handshake_done(self): - BMConnection.state_init(self) - return False - - -class BMServer(AdvancedDispatcher): - def __init__(self, host='127.0.0.1', port=8444): - if not hasattr(self, '_map'): - AdvancedDispatcher.__init__(self) - self.create_socket(socket.AF_INET, socket.SOCK_STREAM) - self.set_reuse_addr() - self.bind((host, port)) - self.listen(5) - - def handle_accept(self): - pair = self.accept() - if pair is not None: - sock, addr = pair - try: - network.connectionpool.BMConnectionPool().addConnection(BMConnection(sock=sock)) - except socket.errno: - pass - - -if __name__ == "__main__": - # initial fill - - for host in (("127.0.0.1", 8448),): - direct = BMConnection(host) - while len(asyncore.socket_map) > 0: - print "loop, state = %s" % (direct.state) - asyncore.loop(timeout=10, count=1) - continue - - proxy = Socks5BMConnection(host) - while len(asyncore.socket_map) > 0: -# print "loop, state = %s" % (proxy.state) - asyncore.loop(timeout=10, count=1) - - proxy = Socks4aBMConnection(host) - while len(asyncore.socket_map) > 0: -# print "loop, state = %s" % (proxy.state) - asyncore.loop(timeout=10, count=1) + AdvancedDispatcher.close(self) diff --git a/src/network/connectionchooser.py b/src/network/connectionchooser.py index 1c8d988d..05ef47bd 100644 --- a/src/network/connectionchooser.py +++ b/src/network/connectionchooser.py @@ -3,7 +3,7 @@ import random from bmconfigparser import BMConfigParser import knownnodes -from queues import portCheckerQueue +from queues import portCheckerQueue, peerDiscoveryQueue import state def chooseConnection(stream): @@ -13,4 +13,7 @@ def chooseConnection(stream): try: return portCheckerQueue.get(False) except Queue.Empty: - return random.choice(knownnodes.knownNodes[stream].keys()) + try: + return peerDiscoveryQueue.get(False) + except Queue.Empty: + return random.choice(knownnodes.knownNodes[stream].keys()) diff --git a/src/network/connectionpool.py b/src/network/connectionpool.py index 8d4f4539..a75b62aa 100644 --- a/src/network/connectionpool.py +++ b/src/network/connectionpool.py @@ -2,11 +2,14 @@ import errno import socket import time import random +import re from bmconfigparser import BMConfigParser from debug import logger import helper_bootstrap import network.bmproto +import network.tcp +import network.udp from network.connectionchooser import chooseConnection import network.asyncore_pollchoose as asyncore import protocol @@ -23,33 +26,31 @@ class BMConnectionPool(object): self.outboundConnections = {} self.inboundConnections = {} self.listeningSockets = {} + self.udpSockets = {} self.streams = [] self.bootstrapped = False def handleReceivedObject(self, connection, streamNumber, hashid): for i in self.inboundConnections.values() + self.outboundConnections.values(): - if not isinstance(i, network.bmproto.BMConnection): + if not isinstance(i, network.bmproto.BMProto): continue + try: + del i.objectsNewToMe[hashid] + except KeyError: + i.objectsNewToThem[hashid] = True if i == connection: try: del i.objectsNewToThem[hashid] except KeyError: pass - else: - try: - del i.objectsNewToThem[hashid] - except KeyError: - i.objectsNewToThem[hashid] = True - try: - del i.objectsNewToMe[hashid] - except KeyError: - pass def connectToStream(self, streamNumber): self.streams.append(streamNumber) def addConnection(self, connection): + if isinstance(connection, network.udp.UDPSocket): + return if connection.isOutbound: self.outboundConnections[connection.destination] = connection else: @@ -59,7 +60,9 @@ class BMConnectionPool(object): self.inboundConnections[connection.destination.host] = connection def removeConnection(self, connection): - if connection.isOutbound: + if isinstance(connection, network.udp.UDPSocket): + return + elif connection.isOutbound: try: del self.outboundConnections[connection.destination] except KeyError: @@ -73,16 +76,29 @@ class BMConnectionPool(object): except KeyError: pass - def startListening(self): - port = BMConfigParser().safeGetInt("bitmessagesettings", "port") + def getListeningIP(self): if BMConfigParser().safeGet("bitmessagesettings", "onionhostname").endswith(".onion"): host = BMConfigParser().safeGet("bitmessagesettigns", "onionbindip") else: host = '127.0.0.1' if BMConfigParser().safeGetBoolean("bitmessagesettings", "sockslisten") or \ BMConfigParser().get("bitmessagesettings", "socksproxytype") == "none": + # python doesn't like bind + INADDR_ANY? + #host = socket.INADDR_ANY host = '' - self.listeningSockets[state.Peer(host, port)] = network.bmproto.BMServer(host=host, port=port) + return host + + def startListening(self): + host = self.getListeningIP() + port = BMConfigParser().safeGetInt("bitmessagesettings", "port") + self.listeningSockets[state.Peer(host, port)] = network.tcp.TCPServer(host=host, port=port) + + def startUDPSocket(self, bind=None): + if bind is None: + host = self.getListeningIP() + self.udpSockets[host] = network.udp.UDPSocket(host=host) + else: + self.udpSockets[bind] = network.udp.UDPSocket(host=bind) def loop(self): # defaults to empty loop if outbound connections are maxed @@ -122,11 +138,11 @@ class BMConnectionPool(object): # continue try: if (BMConfigParser().safeGet("bitmessagesettings", "socksproxytype") == "SOCKS5"): - self.addConnection(network.bmproto.Socks5BMConnection(chosen)) + self.addConnection(network.tcp.Socks5BMConnection(chosen)) elif (BMConfigParser().safeGet("bitmessagesettings", "socksproxytype") == "SOCKS4a"): - self.addConnection(network.bmproto.Socks4aBMConnection(chosen)) + self.addConnection(network.tcp.Socks4aBMConnection(chosen)) elif not chosen.host.endswith(".onion"): - self.addConnection(network.bmproto.BMConnection(chosen)) + self.addConnection(network.tcp.TCPConnection(chosen)) except socket.error as e: if e.errno == errno.ENETUNREACH: continue @@ -134,10 +150,23 @@ class BMConnectionPool(object): if acceptConnections and len(self.listeningSockets) == 0: self.startListening() logger.info('Listening for incoming connections.') + if acceptConnections and len(self.udpSockets) == 0: + if BMConfigParser().safeGet("network", "bind") is None: + self.startUDPSocket() + else: + for bind in re.sub("[^\w.]+", " ", BMConfigParser().safeGet("network", "bind")).split(): + self.startUDPSocket(bind) + logger.info('Starting UDP socket(s).') if len(self.listeningSockets) > 0 and not acceptConnections: for i in self.listeningSockets: i.close() + self.listeningSockets = {} logger.info('Stopped listening for incoming connections.') + if len(self.udpSockets) > 0 and not acceptConnections: + for i in self.udpSockets: + i.close() + self.udpSockets = {} + logger.info('Stopped udp sockets.') # while len(asyncore.socket_map) > 0 and state.shutdown == 0: # print "loop, state = %s" % (proxy.state) diff --git a/src/network/bmqueues.py b/src/network/objectracker.py similarity index 74% rename from src/network/bmqueues.py rename to src/network/objectracker.py index 96ad52e4..246916b9 100644 --- a/src/network/bmqueues.py +++ b/src/network/objectracker.py @@ -1,3 +1,4 @@ +from Queue import Queue import time from inventory import Inventory @@ -21,7 +22,7 @@ except ImportError: # it isn't actually implemented yet so no point in turning it on haveBloom = False -class BMQueues(object): +class ObjectTracker(object): invCleanPeriod = 300 invInitialCapacity = 50000 invErrorRate = 0.03 @@ -29,20 +30,22 @@ class BMQueues(object): def __init__(self): self.objectsNewToMe = {} self.objectsNewToThem = {} + self.downloadPending = 0 + self.downloadQueue = Queue() self.initInvBloom() self.initAddrBloom() def initInvBloom(self): if haveBloom: # lock? - self.invBloom = BloomFilter(capacity=BMQueues.invInitialCapacity, - error_rate=BMQueues.invErrorRate) + self.invBloom = BloomFilter(capacity=ObjectTracker.invInitialCapacity, + error_rate=ObjectTracker.invErrorRate) def initAddrBloom(self): if haveBloom: # lock? - self.addrBloom = BloomFilter(capacity=BMQueues.invInitialCapacity, - error_rate=BMQueues.invErrorRate) + self.addrBloom = BloomFilter(capacity=ObjectTracker.invInitialCapacity, + error_rate=ObjectTracker.invErrorRate) def clean(self): if self.lastcleaned < time.time() - BMQueues.invCleanPeriod: @@ -61,16 +64,17 @@ class BMQueues(object): else: return hashid in self.objectsNewToMe - def handleReceivedObj(self, hashid): + def handleReceivedInventory(self, hashId): if haveBloom: - self.invBloom.add(hashid) - elif hashid in Inventory(): + self.invBloom.add(hashId) + elif hashId in Inventory(): try: - del self.objectsNewToThem[hashid] + del self.objectsNewToThem[hashId] except KeyError: pass else: - self.objectsNewToMe[hashid] = True + self.objectsNewToMe[hashId] = True +# self.DownloadQueue.put(hashId) def hasAddr(self, addr): if haveBloom: @@ -82,6 +86,7 @@ class BMQueues(object): # addr sending -> per node upload queue, and flush every minute or so # inv sending -> if not in bloom, inv immediately, otherwise put into a per node upload queue and flush every minute or so +# data sending -> a simple queue # no bloom # - if inv arrives diff --git a/src/network/receivequeuethread.py b/src/network/receivequeuethread.py index 6405238d..27a01902 100644 --- a/src/network/receivequeuethread.py +++ b/src/network/receivequeuethread.py @@ -1,11 +1,14 @@ import Queue import threading +import time +import addresses from bmconfigparser import BMConfigParser from debug import logger from helper_threading import StoppableThread from inventory import Inventory from network.connectionpool import BMConnectionPool +from network.bmproto import BMProto import protocol class ReceiveQueueThread(threading.Thread, StoppableThread): @@ -14,12 +17,17 @@ class ReceiveQueueThread(threading.Thread, StoppableThread): self.initStop() self.name = "ReceiveQueueThread" BMConnectionPool() - logger.error("init asyncore thread") + logger.error("init receive queue thread") def run(self): + lastprinted = int(time.time()) while not self._stopped: + if lastprinted < int(time.time()): + lastprinted = int(time.time()) processed = 0 for i in BMConnectionPool().inboundConnections.values() + BMConnectionPool().outboundConnections.values(): + if self._stopped: + break try: command, args = i.receiveQueue.get(False) except Queue.Empty: @@ -31,7 +39,7 @@ class ReceiveQueueThread(threading.Thread, StoppableThread): # missing command raise if processed == 0: - self.stop.wait(0.2) + self.stop.wait(2) def command_object(self, connection, objHash): try: @@ -40,5 +48,36 @@ class ReceiveQueueThread(threading.Thread, StoppableThread): connection.antiIntersectionDelay() logger.warning('%s asked for an object with a getdata which is not in either our memory inventory or our SQL inventory. We probably cleaned it out after advertising it but before they got around to asking for it.' % (connection.destination,)) + def command_biginv(self, connection, dummy): + def sendChunk(): + if objectCount == 0: + return + logger.debug('Sending huge inv message with %i objects to just this one peer', objectCount) + connection.writeQueue.put(protocol.CreatePacket('inv', addresses.encodeVarint(objectCount) + payload)) + + # Select all hashes for objects in this stream. + bigInvList = {} + for stream in connection.streams: + for objHash in Inventory().unexpired_hashes_by_stream(stream): + bigInvList[objHash] = 0 + connection.objectsNewToThem[objHash] = True + objectCount = 0 + payload = b'' + # Now let us start appending all of these hashes together. They will be + # sent out in a big inv message to our new peer. + for hash, storedValue in bigInvList.items(): + payload += hash + objectCount += 1 + if objectCount >= BMProto.maxObjectCount: + self.sendChunk() + payload = b'' + objectCount = 0 + + # flush + sendChunk() + + def command_inv(self, connection, hashId): + connection.handleReceivedInventory(hashId) + def stopThread(self): super(ReceiveQueueThread, self).stopThread() diff --git a/src/network/tcp.py b/src/network/tcp.py new file mode 100644 index 00000000..ef54fc18 --- /dev/null +++ b/src/network/tcp.py @@ -0,0 +1,236 @@ +import base64 +from binascii import hexlify +import hashlib +import math +import time +from pprint import pprint +import socket +import struct +import random +import traceback + +from addresses import calculateInventoryHash +from debug import logger +from inventory import Inventory +import knownnodes +from network.advanceddispatcher import AdvancedDispatcher +from network.bmproto import BMProtoError, BMProtoInsufficientDataError, BMProtoExcessiveDataError, BMProto +from network.bmobject import BMObject, BMObjectInsufficientPOWError, BMObjectInvalidDataError, BMObjectExpiredError, BMObjectUnwantedStreamError, BMObjectInvalidError, BMObjectAlreadyHaveError +import network.connectionpool +from network.downloadqueue import DownloadQueue +from network.node import Node +import network.asyncore_pollchoose as asyncore +from network.proxy import Proxy, ProxyError, GeneralProxyError +from network.objectracker import ObjectTracker +from network.socks5 import Socks5Connection, Socks5Resolver, Socks5AuthError, Socks5Error +from network.socks4a import Socks4aConnection, Socks4aResolver, Socks4aError +from network.uploadqueue import UploadQueue, UploadElem, AddrUploadQueue, ObjUploadQueue +from network.tls import TLSDispatcher + +import addresses +from bmconfigparser import BMConfigParser +from queues import objectProcessorQueue, portCheckerQueue, UISignalQueue +import shared +import state +import protocol + +class TCPConnection(BMProto, TLSDispatcher): + def __init__(self, address=None, sock=None): + AdvancedDispatcher.__init__(self, sock) + self.verackReceived = False + self.verackSent = False + self.streams = [0] + self.fullyEstablished = False + self.connectedAt = 0 + self.skipUntil = 0 + if address is None and sock is not None: + self.destination = state.Peer(sock.getpeername()[0], sock.getpeername()[1]) + self.isOutbound = False + TLSDispatcher.__init__(self, sock, server_side=True) + self.connectedAt = time.time() + #print "received connection in background from %s:%i" % (self.destination.host, self.destination.port) + else: + self.destination = address + self.isOutbound = True + if ":" in address.host: + self.create_socket(socket.AF_INET6, socket.SOCK_STREAM) + else: + self.create_socket(socket.AF_INET, socket.SOCK_STREAM) + self.socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) + TLSDispatcher.__init__(self, sock, server_side=False) + self.connect(self.destination) + #print "connecting in background to %s:%i" % (self.destination.host, self.destination.port) + shared.connectedHostsList[self.destination] = 0 + ObjectTracker.__init__(self) + UISignalQueue.put(('updateNetworkStatusTab', 'no data')) + + def state_init(self): + self.bm_proto_reset() + if self.isOutbound: + self.writeQueue.put(protocol.assembleVersionMessage(self.destination.host, self.destination.port, network.connectionpool.BMConnectionPool().streams, False)) + print "%s:%i: Sending version" % (self.destination.host, self.destination.port) + self.set_state("bm_header") + return True + + def antiIntersectionDelay(self, initial = False): + # estimated time for a small object to propagate across the whole network + delay = math.ceil(math.log(max(len(knownnodes.knownNodes[x]) for x in knownnodes.knownNodes) + 2, 20)) * (0.2 + UploadQueue.queueCount/2) + # take the stream with maximum amount of nodes + # +2 is to avoid problems with log(0) and log(1) + # 20 is avg connected nodes count + # 0.2 is avg message transmission time + if delay > 0: + if initial: + self.skipUntil = self.connectedAt + delay + if self.skipUntil > time.time(): + logger.debug("Skipping processing for %.2fs", self.skipUntil - time.time()) + else: + logger.debug("Skipping processing due to missing object for %.2fs", self.skipUntil - time.time()) + self.skipUntil = time.time() + delay + + def set_connection_fully_established(self): + UISignalQueue.put(('updateNetworkStatusTab', 'no data')) + self.antiIntersectionDelay(True) + self.fullyEstablished = True + self.sendAddr() + self.sendBigInv() + + def sendAddr(self): + # We are going to share a maximum number of 1000 addrs (per overlapping + # stream) with our peer. 500 from overlapping streams, 250 from the + # left child stream, and 250 from the right child stream. + maxAddrCount = BMConfigParser().safeGetInt("bitmessagesettings", "maxaddrperstreamsend", 500) + + # init + addressCount = 0 + payload = b'' + + templist = [] + addrs = {} + for stream in self.streams: + with knownnodes.knownNodesLock: + if len(knownnodes.knownNodes[stream]) > 0: + filtered = {k: v for k, v in knownnodes.knownNodes[stream].items() + if v > (int(time.time()) - shared.maximumAgeOfNodesThatIAdvertiseToOthers)} + elemCount = len(filtered) + if elemCount > maxAddrCount: + elemCount = maxAddrCount + # only if more recent than 3 hours + addrs[stream] = random.sample(filtered.items(), elemCount) + # sent 250 only if the remote isn't interested in it + if len(knownnodes.knownNodes[stream * 2]) > 0 and stream not in self.streams: + filtered = {k: v for k, v in knownnodes.knownNodes[stream*2].items() + if v > (int(time.time()) - shared.maximumAgeOfNodesThatIAdvertiseToOthers)} + elemCount = len(filtered) + if elemCount > maxAddrCount / 2: + elemCount = int(maxAddrCount / 2) + addrs[stream * 2] = random.sample(filtered.items(), elemCount) + if len(knownnodes.knownNodes[(stream * 2) + 1]) > 0 and stream not in self.streams: + filtered = {k: v for k, v in knownnodes.knownNodes[stream*2+1].items() + if v > (int(time.time()) - shared.maximumAgeOfNodesThatIAdvertiseToOthers)} + elemCount = len(filtered) + if elemCount > maxAddrCount / 2: + elemCount = int(maxAddrCount / 2) + addrs[stream * 2 + 1] = random.sample(filtered.items(), elemCount) + for substream in addrs.keys(): + for peer, timestamp in addrs[substream]: + templist.append((substream, peer, timestamp)) + if len(templist) >= BMProto.maxAddrCount: + self.writeQueue.put(BMProto.assembleAddr(templist)) + templist = [] + # flush + if len(templist) > 0: + self.writeQueue.put(BMProto.assembleAddr(templist)) + + def sendBigInv(self): + self.receiveQueue.put(("biginv", None)) + + def handle_connect_event(self): + try: + asyncore.dispatcher.handle_connect_event(self) + self.connectedAt = time.time() + except socket.error as e: + #print "%s:%i: socket error: %s" % (self.destination.host, self.destination.port, str(e)) + self.close() + + def handle_read_event(self): + try: + asyncore.dispatcher.handle_read_event(self) + except socket.error as e: + #print "%s:%i: socket error: %s" % (self.destination.host, self.destination.port, str(e)) + self.close() + + def handle_write_event(self): + try: + asyncore.dispatcher.handle_write_event(self) + except socket.error as e: + #print "%s:%i: socket error: %s" % (self.destination.host, self.destination.port, str(e)) + self.close() + + def close(self, reason=None): + self.set_state("close") +# if reason is None: +# print "%s:%i: closing" % (self.destination.host, self.destination.port) +# #traceback.print_stack() +# else: +# print "%s:%i: closing, %s" % (self.destination.host, self.destination.port, reason) + network.connectionpool.BMConnectionPool().removeConnection(self) + asyncore.dispatcher.close(self) + + +class Socks5BMConnection(Socks5Connection, TCPConnection): + def __init__(self, address): + Socks5Connection.__init__(self, address=address) + + def state_socks_handshake_done(self): + TCPConnection.state_init(self) + return False + + +class Socks4aBMConnection(Socks4aConnection, TCPConnection): + def __init__(self, address): + Socks4aConnection.__init__(self, address=address) + + def state_socks_handshake_done(self): + TCPConnection.state_init(self) + return False + + +class TCPServer(AdvancedDispatcher): + def __init__(self, host='127.0.0.1', port=8444): + if not hasattr(self, '_map'): + AdvancedDispatcher.__init__(self) + self.create_socket(socket.AF_INET, socket.SOCK_STREAM) + self.set_reuse_addr() + self.bind((host, port)) + self.listen(5) + + def handle_accept(self): + pair = self.accept() + if pair is not None: + sock, addr = pair + try: + network.connectionpool.BMConnectionPool().addConnection(TCPConnection(sock=sock)) + except socket.error: + pass + + +if __name__ == "__main__": + # initial fill + + for host in (("127.0.0.1", 8448),): + direct = TCPConnection(host) + while len(asyncore.socket_map) > 0: + print "loop, state = %s" % (direct.state) + asyncore.loop(timeout=10, count=1) + continue + + proxy = Socks5BMConnection(host) + while len(asyncore.socket_map) > 0: +# print "loop, state = %s" % (proxy.state) + asyncore.loop(timeout=10, count=1) + + proxy = Socks4aBMConnection(host) + while len(asyncore.socket_map) > 0: +# print "loop, state = %s" % (proxy.state) + asyncore.loop(timeout=10, count=1) diff --git a/src/network/tls.py b/src/network/tls.py index d2abb6b9..f79f0650 100644 --- a/src/network/tls.py +++ b/src/network/tls.py @@ -60,7 +60,7 @@ class TLSDispatcher(AdvancedDispatcher): def writable(self): try: - if self.tlsStarted and not self.tlsDone and len(self.write_buf) == 0: + if self.tlsStarted and not self.tlsDone and len(self.write_buf) == 0 and self.writeQueue.empty(): #print "tls writable, %r" % (self.want_write) return self.want_write else: @@ -70,7 +70,7 @@ class TLSDispatcher(AdvancedDispatcher): def readable(self): try: - if self.tlsStarted and not self.tlsDone and len(self.write_buf) == 0: + if self.tlsStarted and not self.tlsDone and len(self.write_buf) == 0 and self.writeQueue.empty(): #print "tls readable, %r" % (self.want_read) return self.want_read else: @@ -81,7 +81,7 @@ class TLSDispatcher(AdvancedDispatcher): def handle_read(self): try: # wait for write buffer flush - if self.tlsStarted and not self.tlsDone and len(self.write_buf) == 0: + if self.tlsStarted and not self.tlsDone and len(self.write_buf) == 0 and self.writeQueue.empty(): #print "handshaking (read)" self.state_tls_handshake() else: @@ -93,7 +93,7 @@ class TLSDispatcher(AdvancedDispatcher): def handle_write(self): try: # wait for write buffer flush - if self.tlsStarted and not self.tlsDone and len(self.write_buf) == 0: + if self.tlsStarted and not self.tlsDone and len(self.write_buf) == 0 and self.writeQueue.empty(): #print "handshaking (write)" self.state_tls_handshake() else: diff --git a/src/network/udp.py b/src/network/udp.py new file mode 100644 index 00000000..81bcc06a --- /dev/null +++ b/src/network/udp.py @@ -0,0 +1,198 @@ +import base64 +from binascii import hexlify +import hashlib +import math +import time +from pprint import pprint +import socket +import struct +import random +import traceback + +from addresses import calculateInventoryHash +from debug import logger +from inventory import Inventory +import knownnodes +from network.advanceddispatcher import AdvancedDispatcher +from network.bmproto import BMProtoError, BMProtoInsufficientDataError, BMProtoExcessiveDataError, BMProto +from network.bmobject import BMObject, BMObjectInsufficientPOWError, BMObjectInvalidDataError, BMObjectExpiredError, BMObjectUnwantedStreamError, BMObjectInvalidError, BMObjectAlreadyHaveError +import network.connectionpool +from network.downloadqueue import DownloadQueue +from network.node import Node +import network.asyncore_pollchoose as asyncore +from network.objectracker import ObjectTracker +from network.uploadqueue import UploadQueue, UploadElem, AddrUploadQueue, ObjUploadQueue + +import addresses +from bmconfigparser import BMConfigParser +from queues import objectProcessorQueue, peerDiscoveryQueue, portCheckerQueue, UISignalQueue +import shared +import state +import protocol + +class UDPSocket(BMProto): + port = 8444 + announceInterval = 60 + + def __init__(self, host=None, sock=None): + AdvancedDispatcher.__init__(self, sock) + self.verackReceived = True + self.verackSent = True + # TODO sort out streams + self.streams = [1] + self.fullyEstablished = True + self.connectedAt = 0 + self.skipUntil = 0 + self.isOutbound = False + if sock is None: + if host is None: + host = '' + if ":" in host: + self.create_socket(socket.AF_INET6, socket.SOCK_DGRAM) + else: + self.create_socket(socket.AF_INET, socket.SOCK_DGRAM) + print "binding to %s" % (host) + self.socket.bind((host, UDPSocket.port)) + #BINDTODEVICE is only available on linux and requires root + #try: + #print "binding to %s" % (host) + #self.socket.setsockopt(socket.SOL_SOCKET, socket.SO_BINDTODEVICE, host) + #except AttributeError: + else: + self.socket = sock + self.socket.setsockopt(socket.SOL_SOCKET, socket.SO_BROADCAST, 1) + self.socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) + self.socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEPORT, 1) + self.destination = state.Peer(self.socket.getsockname()[0], self.socket.getsockname()[1]) + ObjectTracker.__init__(self) + self.connecting = False + self.connected = True + # packet was received from a local IP + self.local = False + self.set_state("bm_header") + + # disable most commands before doing research / testing + # only addr (peer discovery), error and object are implemented + + def bm_command_error(self): + return BMProto.bm_command_error(self) + + def bm_command_getdata(self): + return True +# return BMProto.bm_command_getdata(self) + + def bm_command_inv(self): + return True +# return BMProto.bm_command_inv(self) + + def bm_command_object(self): + return BMProto.bm_command_object(self) + + def bm_command_addr(self): +# BMProto.bm_command_object(self) + addresses = self._decode_addr() + # only allow peer discovery from private IPs in order to avoid attacks from random IPs on the internet + if not self.local: + return + remoteport = False + for i in addresses: + seenTime, stream, services, ip, port = i + decodedIP = protocol.checkIPAddress(ip) + if stream not in state.streamsInWhichIAmParticipating: + continue + if seenTime < time.time() - BMProto.maxtimeOffset or seenTime > time.time() + BMProto.maxTimeOffset: + continue + if decodedIP is False: + # if the address isn't local, interpret it as the hosts' own announcement + remoteport = port + if remoteport is False: + return + print "received peer discovery from %s:%i (port %i):" % (self.destination.host, self.destination.port, remoteport) + if self.local: + peerDiscoveryQueue.put(state.peer(self.destination.host, remoteport)) + return True + + def bm_command_portcheck(self): + return True + + def bm_command_ping(self): + return True + + def bm_command_pong(self): + return True + + def bm_command_verack(self): + return True + + def bm_command_version(self): + return True + + def handle_connect_event(self): + return + + def writable(self): + return not self.writeQueue.empty() + + def readable(self): + return len(self.read_buf) < AdvancedDispatcher._buf_len + + def handle_read(self): + print "read!" + try: + (addr, recdata) = self.socket.recvfrom(AdvancedDispatcher._buf_len) + except socket.error as e: + print "socket error: %s" % (str(e)) + return + + self.destination = state.Peer(addr[0], addr[1]) + encodedAddr = socket.inet_pton(self.socket.family, addr[0]) + if protocol.checkIPAddress(encodedAddr, True): + self.local = True + else: + self.local = False + # overwrite the old buffer to avoid mixing data and so that self.local works correctly + self.read_buf = data + self.process() + + def handle_write(self): +# print "handling write" + try: + data = self.writeQueue.get(False) + except Queue.Empty: + return + try: + retval = self.socket.sendto(data, ('', UDPSocket.port)) +# print "broadcasted %ib" % (retval) + except socket.error as e: + print "socket error on sendato: %s" % (e) + + def close(self, reason=None): + self.set_state("close") +# if reason is None: +# print "%s:%i: closing" % (self.destination.host, self.destination.port) +# #traceback.print_stack() +# else: +# print "%s:%i: closing, %s" % (self.destination.host, self.destination.port, reason) + network.connectionpool.BMConnectionPool().removeConnection(self) + asyncore.dispatcher.close(self) + + +if __name__ == "__main__": + # initial fill + + for host in (("127.0.0.1", 8448),): + direct = BMConnection(host) + while len(asyncore.socket_map) > 0: + print "loop, state = %s" % (direct.state) + asyncore.loop(timeout=10, count=1) + continue + + proxy = Socks5BMConnection(host) + while len(asyncore.socket_map) > 0: +# print "loop, state = %s" % (proxy.state) + asyncore.loop(timeout=10, count=1) + + proxy = Socks4aBMConnection(host) + while len(asyncore.socket_map) > 0: +# print "loop, state = %s" % (proxy.state) + asyncore.loop(timeout=10, count=1) diff --git a/src/protocol.py b/src/protocol.py index 83ecb7bd..5661dff0 100644 --- a/src/protocol.py +++ b/src/protocol.py @@ -86,13 +86,15 @@ def networkType(host): else: return 'IPv6' -def checkIPAddress(host): +def checkIPAddress(host, private=False): if host[0:12] == '\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xFF\xFF': hostStandardFormat = socket.inet_ntop(socket.AF_INET, host[12:]) - return checkIPv4Address(host[12:], hostStandardFormat) + return checkIPv4Address(host[12:], hostStandardFormat, private) elif host[0:6] == '\xfd\x87\xd8\x7e\xeb\x43': # Onion, based on BMD/bitcoind hostStandardFormat = base64.b32encode(host[6:]).lower() + ".onion" + if private: + return False return hostStandardFormat else: hostStandardFormat = socket.inet_ntop(socket.AF_INET6, host) @@ -100,34 +102,34 @@ def checkIPAddress(host): # This can happen on Windows systems which are not 64-bit compatible # so let us drop the IPv6 address. return False - return checkIPv6Address(host, hostStandardFormat) + return checkIPv6Address(host, hostStandardFormat, private) -def checkIPv4Address(host, hostStandardFormat): +def checkIPv4Address(host, hostStandardFormat, private=False): if host[0] == '\x7F': # 127/8 logger.debug('Ignoring IP address in loopback range: ' + hostStandardFormat) return False if host[0] == '\x0A': # 10/8 logger.debug('Ignoring IP address in private range: ' + hostStandardFormat) - return False + return hostStandardFormat if private else False if host[0:2] == '\xC0\xA8': # 192.168/16 logger.debug('Ignoring IP address in private range: ' + hostStandardFormat) - return False + return hostStandardFormat if private else False if host[0:2] >= '\xAC\x10' and host[0:2] < '\xAC\x20': # 172.16/12 logger.debug('Ignoring IP address in private range:' + hostStandardFormat) return False - return hostStandardFormat + return False if private else hostStandardFormat -def checkIPv6Address(host, hostStandardFormat): +def checkIPv6Address(host, hostStandardFormat, private=False): if host == ('\x00' * 15) + '\x01': logger.debug('Ignoring loopback address: ' + hostStandardFormat) return False if host[0] == '\xFE' and (ord(host[1]) & 0xc0) == 0x80: logger.debug ('Ignoring local address: ' + hostStandardFormat) - return False + return hostStandardFormat if private else False if (ord(host[0]) & 0xfe) == 0xfc: logger.debug ('Ignoring unique local address: ' + hostStandardFormat) - return False - return hostStandardFormat + return hostStandardFormat if private else False + return False if private else hostStandardFormat # checks diff --git a/src/queues.py b/src/queues.py index c6b09307..a11bedeb 100644 --- a/src/queues.py +++ b/src/queues.py @@ -7,5 +7,6 @@ addressGeneratorQueue = Queue.Queue() # receiveDataThreads dump objects they hear on the network into this queue to be processed. objectProcessorQueue = ObjectProcessorQueue() portCheckerQueue = Queue.Queue() +peerDiscoveryQueue = Queue.Queue() apiAddressGeneratorReturnQueue = Queue.Queue( ) # The address generator thread uses this queue to get information back to the API thread. From fa9ad537a576588f2f5f49e316dee417ed5c4dae Mon Sep 17 00:00:00 2001 From: Peter Surda Date: Sat, 27 May 2017 19:39:19 +0200 Subject: [PATCH 061/407] Add task_done to asyncore-related queues --- src/network/advanceddispatcher.py | 3 +++ src/network/connectionchooser.py | 7 +++++-- src/network/receivequeuethread.py | 2 ++ src/network/udp.py | 5 +++-- 4 files changed, 13 insertions(+), 4 deletions(-) diff --git a/src/network/advanceddispatcher.py b/src/network/advanceddispatcher.py index 938eb11d..947824bd 100644 --- a/src/network/advanceddispatcher.py +++ b/src/network/advanceddispatcher.py @@ -80,6 +80,7 @@ class AdvancedDispatcher(asyncore.dispatcher): while len(self.write_buf) < bufSize: try: self.write_buf += self.writeQueue.get(False) + self.writeQueue.task_done() except Queue.Empty: break if len(self.write_buf) > 0: @@ -103,11 +104,13 @@ class AdvancedDispatcher(asyncore.dispatcher): while True: try: self.writeQueue.get(False) + self.writeQueue.task_done() except Queue.Empty: break while True: try: self.receiveQueue.get(False) + self.receiveQueue.task_done() except Queue.Empty: break asyncore.dispatcher.close(self) diff --git a/src/network/connectionchooser.py b/src/network/connectionchooser.py index 05ef47bd..1e26b994 100644 --- a/src/network/connectionchooser.py +++ b/src/network/connectionchooser.py @@ -11,9 +11,12 @@ def chooseConnection(stream): return state.trustedPeer else: try: - return portCheckerQueue.get(False) + retval = portCheckerQueue.get(False) + portCheckerQueue.task_done() except Queue.Empty: try: - return peerDiscoveryQueue.get(False) + retval = peerDiscoveryQueue.get(False) + peerDiscoveryQueue.task_done() except Queue.Empty: return random.choice(knownnodes.knownNodes[stream].keys()) + return retval diff --git a/src/network/receivequeuethread.py b/src/network/receivequeuethread.py index 27a01902..c5509b65 100644 --- a/src/network/receivequeuethread.py +++ b/src/network/receivequeuethread.py @@ -35,7 +35,9 @@ class ReceiveQueueThread(threading.Thread, StoppableThread): processed += 1 try: getattr(self, "command_" + str(command))(i, args) + i.receiveQueue.task_done() except AttributeError: + i.receiveQueue.task_done() # missing command raise if processed == 0: diff --git a/src/network/udp.py b/src/network/udp.py index 81bcc06a..4bb78823 100644 --- a/src/network/udp.py +++ b/src/network/udp.py @@ -137,7 +137,6 @@ class UDPSocket(BMProto): return len(self.read_buf) < AdvancedDispatcher._buf_len def handle_read(self): - print "read!" try: (addr, recdata) = self.socket.recvfrom(AdvancedDispatcher._buf_len) except socket.error as e: @@ -150,6 +149,7 @@ class UDPSocket(BMProto): self.local = True else: self.local = False + print "read %ib" % (len(recdata)) # overwrite the old buffer to avoid mixing data and so that self.local works correctly self.read_buf = data self.process() @@ -162,9 +162,10 @@ class UDPSocket(BMProto): return try: retval = self.socket.sendto(data, ('', UDPSocket.port)) -# print "broadcasted %ib" % (retval) + print "broadcasted %ib" % (retval) except socket.error as e: print "socket error on sendato: %s" % (e) + self.writeQueue.task_done() def close(self, reason=None): self.set_state("close") From 99e714c43225825ccde731c038cb389f059e0c2a Mon Sep 17 00:00:00 2001 From: Peter Surda Date: Sat, 27 May 2017 20:43:27 +0200 Subject: [PATCH 062/407] UDP socket bugfixes --- src/network/udp.py | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/src/network/udp.py b/src/network/udp.py index 4bb78823..42f8bd18 100644 --- a/src/network/udp.py +++ b/src/network/udp.py @@ -100,7 +100,7 @@ class UDPSocket(BMProto): decodedIP = protocol.checkIPAddress(ip) if stream not in state.streamsInWhichIAmParticipating: continue - if seenTime < time.time() - BMProto.maxtimeOffset or seenTime > time.time() + BMProto.maxTimeOffset: + if seenTime < time.time() - BMProto.maxTimeOffset or seenTime > time.time() + BMProto.maxTimeOffset: continue if decodedIP is False: # if the address isn't local, interpret it as the hosts' own announcement @@ -109,7 +109,7 @@ class UDPSocket(BMProto): return print "received peer discovery from %s:%i (port %i):" % (self.destination.host, self.destination.port, remoteport) if self.local: - peerDiscoveryQueue.put(state.peer(self.destination.host, remoteport)) + peerDiscoveryQueue.put(state.Peer(self.destination.host, remoteport)) return True def bm_command_portcheck(self): @@ -138,20 +138,21 @@ class UDPSocket(BMProto): def handle_read(self): try: - (addr, recdata) = self.socket.recvfrom(AdvancedDispatcher._buf_len) + (recdata, addr) = self.socket.recvfrom(AdvancedDispatcher._buf_len) except socket.error as e: print "socket error: %s" % (str(e)) return self.destination = state.Peer(addr[0], addr[1]) - encodedAddr = socket.inet_pton(self.socket.family, addr[0]) + encodedAddr = protocol.encodeHost(addr[0]) if protocol.checkIPAddress(encodedAddr, True): self.local = True else: self.local = False print "read %ib" % (len(recdata)) # overwrite the old buffer to avoid mixing data and so that self.local works correctly - self.read_buf = data + self.read_buf = recdata + self.bm_proto_reset() self.process() def handle_write(self): From 21f6d38ec260ea1d8209da014977a9ba93e17916 Mon Sep 17 00:00:00 2001 From: Peter Surda Date: Sat, 27 May 2017 21:52:56 +0200 Subject: [PATCH 063/407] Asyncore fixes - TCP fixes --- src/network/advanceddispatcher.py | 4 ++-- src/network/asyncore_pollchoose.py | 4 ++-- src/network/bmproto.py | 22 -------------------- src/network/tcp.py | 33 +++++++++--------------------- src/network/udp.py | 12 +---------- 5 files changed, 15 insertions(+), 60 deletions(-) diff --git a/src/network/advanceddispatcher.py b/src/network/advanceddispatcher.py index 947824bd..8c976b36 100644 --- a/src/network/advanceddispatcher.py +++ b/src/network/advanceddispatcher.py @@ -34,7 +34,7 @@ class AdvancedDispatcher(asyncore.dispatcher): return True def process(self): - if self.state not in ["init", "tls_handshake"] and len(self.read_buf) == 0: + if self.state != "tls_handshake" and len(self.read_buf) == 0: return if not self.connected: return @@ -54,7 +54,7 @@ class AdvancedDispatcher(asyncore.dispatcher): self.state = state def writable(self): - return self.connecting or len(self.write_buf) > 0 or not self.writeQueue.empty() + return self.connected and (len(self.write_buf) > 0 or not self.writeQueue.empty()) def readable(self): return self.connecting or len(self.read_buf) < AdvancedDispatcher._buf_len diff --git a/src/network/asyncore_pollchoose.py b/src/network/asyncore_pollchoose.py index 08ee42d0..dda6d7c2 100644 --- a/src/network/asyncore_pollchoose.py +++ b/src/network/asyncore_pollchoose.py @@ -57,7 +57,7 @@ import warnings import os from errno import EALREADY, EINPROGRESS, EWOULDBLOCK, ECONNRESET, EINVAL, \ ENOTCONN, ESHUTDOWN, EISCONN, EBADF, ECONNABORTED, EPIPE, EAGAIN, \ - ECONNREFUSED, \ + ECONNREFUSED, EHOSTUNREACH, \ errorcode try: from errno import WSAEWOULDBLOCK @@ -66,7 +66,7 @@ except: from ssl import SSLError, SSL_ERROR_WANT_READ, SSL_ERROR_WANT_WRITE _DISCONNECTED = frozenset((ECONNRESET, ENOTCONN, ESHUTDOWN, ECONNABORTED, EPIPE, - EBADF, ECONNREFUSED)) + EBADF, ECONNREFUSED, EHOSTUNREACH)) OP_READ = 1 OP_WRITE = 2 diff --git a/src/network/bmproto.py b/src/network/bmproto.py index 6e1c3a18..c706b81a 100644 --- a/src/network/bmproto.py +++ b/src/network/bmproto.py @@ -469,28 +469,6 @@ class BMProto(AdvancedDispatcher, ObjectTracker): payload += struct.pack('>H', peer.port) # remote port return protocol.CreatePacket('addr', payload) - def handle_connect_event(self): - try: - asyncore.dispatcher.handle_connect_event(self) - self.connectedAt = time.time() - except socket.error as e: - #print "%s:%i: socket error: %s" % (self.destination.host, self.destination.port, str(e)) - self.close() - - def handle_read_event(self): - try: - asyncore.dispatcher.handle_read_event(self) - except socket.error as e: - #print "%s:%i: socket error: %s" % (self.destination.host, self.destination.port, str(e)) - self.close() - - def handle_write_event(self): - try: - asyncore.dispatcher.handle_write_event(self) - except socket.error as e: - #print "%s:%i: socket error: %s" % (self.destination.host, self.destination.port, str(e)) - self.close() - def close(self, reason=None): self.set_state("close") # if reason is None: diff --git a/src/network/tcp.py b/src/network/tcp.py index ef54fc18..8f7e60d7 100644 --- a/src/network/tcp.py +++ b/src/network/tcp.py @@ -63,14 +63,8 @@ class TCPConnection(BMProto, TLSDispatcher): shared.connectedHostsList[self.destination] = 0 ObjectTracker.__init__(self) UISignalQueue.put(('updateNetworkStatusTab', 'no data')) - - def state_init(self): self.bm_proto_reset() - if self.isOutbound: - self.writeQueue.put(protocol.assembleVersionMessage(self.destination.host, self.destination.port, network.connectionpool.BMConnectionPool().streams, False)) - print "%s:%i: Sending version" % (self.destination.host, self.destination.port) self.set_state("bm_header") - return True def antiIntersectionDelay(self, initial = False): # estimated time for a small object to propagate across the whole network @@ -148,35 +142,28 @@ class TCPConnection(BMProto, TLSDispatcher): def handle_connect_event(self): try: asyncore.dispatcher.handle_connect_event(self) - self.connectedAt = time.time() except socket.error as e: - #print "%s:%i: socket error: %s" % (self.destination.host, self.destination.port, str(e)) - self.close() + if e.errno in asyncore._DISCONNECTED: + self.close("Connection failed") + return + self.writeQueue.put(protocol.assembleVersionMessage(self.destination.host, self.destination.port, network.connectionpool.BMConnectionPool().streams, False)) + #print "%s:%i: Sending version" % (self.destination.host, self.destination.port) + self.connectedAt = time.time() - def handle_read_event(self): + def handle_read(self): try: - asyncore.dispatcher.handle_read_event(self) + AdvancedDispatcher.handle_read(self) except socket.error as e: #print "%s:%i: socket error: %s" % (self.destination.host, self.destination.port, str(e)) self.close() - def handle_write_event(self): + def handle_write(self): try: - asyncore.dispatcher.handle_write_event(self) + AdvancedDispatcher.handle_write(self) except socket.error as e: #print "%s:%i: socket error: %s" % (self.destination.host, self.destination.port, str(e)) self.close() - def close(self, reason=None): - self.set_state("close") -# if reason is None: -# print "%s:%i: closing" % (self.destination.host, self.destination.port) -# #traceback.print_stack() -# else: -# print "%s:%i: closing, %s" % (self.destination.host, self.destination.port, reason) - network.connectionpool.BMConnectionPool().removeConnection(self) - asyncore.dispatcher.close(self) - class Socks5BMConnection(Socks5Connection, TCPConnection): def __init__(self, address): diff --git a/src/network/udp.py b/src/network/udp.py index 42f8bd18..9e687603 100644 --- a/src/network/udp.py +++ b/src/network/udp.py @@ -127,7 +127,7 @@ class UDPSocket(BMProto): def bm_command_version(self): return True - def handle_connect_event(self): + def handle_connect(self): return def writable(self): @@ -168,16 +168,6 @@ class UDPSocket(BMProto): print "socket error on sendato: %s" % (e) self.writeQueue.task_done() - def close(self, reason=None): - self.set_state("close") -# if reason is None: -# print "%s:%i: closing" % (self.destination.host, self.destination.port) -# #traceback.print_stack() -# else: -# print "%s:%i: closing, %s" % (self.destination.host, self.destination.port, reason) - network.connectionpool.BMConnectionPool().removeConnection(self) - asyncore.dispatcher.close(self) - if __name__ == "__main__": # initial fill From 5d4e1e2007c7cb40abdd16ffd89a12873282ccfd Mon Sep 17 00:00:00 2001 From: Peter Surda Date: Sat, 27 May 2017 22:30:30 +0200 Subject: [PATCH 064/407] asyncore fixes - bm headers and commands are only read up to expected length. On a very fast connection (e.g. local VM), reading verack also read a part of the TLS handshake - some debugging info moved from print to logger.debug - tls handshake cleanup --- src/network/advanceddispatcher.py | 17 ++++++++++++----- src/network/bmproto.py | 17 +++++++++-------- src/network/tcp.py | 8 ++++---- src/network/tls.py | 12 ++++++------ 4 files changed, 31 insertions(+), 23 deletions(-) diff --git a/src/network/advanceddispatcher.py b/src/network/advanceddispatcher.py index 8c976b36..96b206cf 100644 --- a/src/network/advanceddispatcher.py +++ b/src/network/advanceddispatcher.py @@ -18,6 +18,7 @@ class AdvancedDispatcher(asyncore.dispatcher): self.lastTx = time.time() self.sentBytes = 0 self.receivedBytes = 0 + self.expectBytes = 0 def slice_write_buf(self, length=0): if length > 0: @@ -49,24 +50,30 @@ class AdvancedDispatcher(asyncore.dispatcher): raise maxLoop -= 1 - def set_state(self, state, length=0): + def set_state(self, state, length=0, expectBytes=0): + self.expectBytes = expectBytes self.slice_read_buf(length) self.state = state def writable(self): - return self.connected and (len(self.write_buf) > 0 or not self.writeQueue.empty()) + return self.connecting or len(self.write_buf) > 0 or not self.writeQueue.empty() def readable(self): return self.connecting or len(self.read_buf) < AdvancedDispatcher._buf_len def handle_read(self): self.lastTx = time.time() + downloadBytes = AdvancedDispatcher._buf_len + if asyncore.maxDownloadRate > 0: + downloadBytes = asyncore.downloadChunk + if self.expectBytes > 0 and downloadBytes > self.expectBytes: + downloadBytes = self.expectBytes + newData = self.recv(downloadBytes) if asyncore.maxDownloadRate > 0: - newData = self.recv(asyncore.downloadChunk) asyncore.downloadBucket -= len(newData) - else: - newData = self.recv(AdvancedDispatcher._buf_len) self.receivedBytes += len(newData) + if self.expectBytes > 0: + self.expectBytes -= len(newData) asyncore.updateReceived(len(newData)) self.read_buf += newData self.process() diff --git a/src/network/bmproto.py b/src/network/bmproto.py index c706b81a..d3f02568 100644 --- a/src/network/bmproto.py +++ b/src/network/bmproto.py @@ -91,6 +91,7 @@ class BMProto(AdvancedDispatcher, ObjectTracker): self.payload = None self.invalid = False self.payloadOffset = 0 + self.expectBytes = protocol.Header.size self.object = None def state_bm_header(self): @@ -109,7 +110,7 @@ class BMProto(AdvancedDispatcher, ObjectTracker): return False if self.payloadLength > BMProto.maxMessageSize: self.invalid = True - self.set_state("bm_command", protocol.Header.size) + self.set_state("bm_command", protocol.Header.size, expectBytes=self.payloadLength) return True def state_bm_command(self): @@ -376,13 +377,13 @@ class BMProto(AdvancedDispatcher, ObjectTracker): self.remoteProtocolVersion, self.services, self.timestamp, self.sockNode, self.peerNode, self.nonce, self.userAgent, self.streams = self.decode_payload_content("IQQiiQlslv") self.nonce = struct.pack('>Q', self.nonce) self.timeOffset = self.timestamp - int(time.time()) - #print "remoteProtocolVersion: %i" % (self.remoteProtocolVersion) - #print "services: %08X" % (self.services) - #print "time offset: %i" % (self.timestamp - int(time.time())) - #print "my external IP: %s" % (self.sockNode.host) - #print "remote node incoming port: %i" % (self.peerNode.port) - #print "user agent: %s" % (self.userAgent) - #print "streams: [%s]" % (",".join(map(str,self.streams))) + logger.debug("remoteProtocolVersion: %i", self.remoteProtocolVersion) + logger.debug("services: %08X", self.services) + logger.debug("time offset: %i", self.timestamp - int(time.time())) + logger.debug("my external IP: %s", self.sockNode.host) + logger.debug("remote node incoming port: %i", self.peerNode.port) + logger.debug("user agent: %s", self.userAgent) + logger.debug("streams: [%s]", ",".join(map(str,self.streams))) if not self.peerValidityChecks(): # TODO ABORT return True diff --git a/src/network/tcp.py b/src/network/tcp.py index 8f7e60d7..8c5fb968 100644 --- a/src/network/tcp.py +++ b/src/network/tcp.py @@ -48,7 +48,7 @@ class TCPConnection(BMProto, TLSDispatcher): self.isOutbound = False TLSDispatcher.__init__(self, sock, server_side=True) self.connectedAt = time.time() - #print "received connection in background from %s:%i" % (self.destination.host, self.destination.port) + logger.debug("Received connection from %s:%i", self.destination.host, self.destination.port) else: self.destination = address self.isOutbound = True @@ -59,7 +59,7 @@ class TCPConnection(BMProto, TLSDispatcher): self.socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) TLSDispatcher.__init__(self, sock, server_side=False) self.connect(self.destination) - #print "connecting in background to %s:%i" % (self.destination.host, self.destination.port) + logger.debug("Connecting to %s:%i", self.destination.host, self.destination.port) shared.connectedHostsList[self.destination] = 0 ObjectTracker.__init__(self) UISignalQueue.put(('updateNetworkStatusTab', 'no data')) @@ -152,14 +152,14 @@ class TCPConnection(BMProto, TLSDispatcher): def handle_read(self): try: - AdvancedDispatcher.handle_read(self) + TLSDispatcher.handle_read(self) except socket.error as e: #print "%s:%i: socket error: %s" % (self.destination.host, self.destination.port, str(e)) self.close() def handle_write(self): try: - AdvancedDispatcher.handle_write(self) + TLSDispatcher.handle_write(self) except socket.error as e: #print "%s:%i: socket error: %s" % (self.destination.host, self.destination.port, str(e)) self.close() diff --git a/src/network/tls.py b/src/network/tls.py index f79f0650..f813e3be 100644 --- a/src/network/tls.py +++ b/src/network/tls.py @@ -54,7 +54,7 @@ class TLSDispatcher(AdvancedDispatcher): do_handshake_on_connect=False) self.sslSocket.setblocking(0) self.want_read = self.want_write = True - self.set_state("tls_handshake") + self.set_state("bm_header") # if hasattr(self.socket, "context"): # self.socket.context.set_ecdh_curve("secp256k1") @@ -83,7 +83,7 @@ class TLSDispatcher(AdvancedDispatcher): # wait for write buffer flush if self.tlsStarted and not self.tlsDone and len(self.write_buf) == 0 and self.writeQueue.empty(): #print "handshaking (read)" - self.state_tls_handshake() + self.tls_handshake() else: #print "not handshaking (read)" return AdvancedDispatcher.handle_read(self) @@ -95,23 +95,23 @@ class TLSDispatcher(AdvancedDispatcher): # wait for write buffer flush if self.tlsStarted and not self.tlsDone and len(self.write_buf) == 0 and self.writeQueue.empty(): #print "handshaking (write)" - self.state_tls_handshake() + self.tls_handshake() else: #print "not handshaking (write)" return AdvancedDispatcher.handle_write(self) except AttributeError: return AdvancedDispatcher.handle_read(self) - def state_tls_handshake(self): + def tls_handshake(self): # wait for flush if len(self.write_buf) > 0: return False # Perform the handshake. try: - #print "handshaking (internal)" + print "handshaking (internal)" self.sslSocket.do_handshake() except ssl.SSLError, err: - #print "%s:%i: handshake fail" % (self.destination.host, self.destination.port) + print "%s:%i: handshake fail" % (self.destination.host, self.destination.port) self.want_read = self.want_write = False if err.args[0] == ssl.SSL_ERROR_WANT_READ: #print "want read" From c85d52b8e8e8d6dfd9e67becab4cd53e8630b607 Mon Sep 17 00:00:00 2001 From: Peter Surda Date: Mon, 29 May 2017 00:24:07 +0200 Subject: [PATCH 065/407] Asyncore updates - asyncore is now on by default - inv announcements implemented - bandwidth limit implemented / fixed - stats on download / upload speed now work - make prints into logger - limit knownNodes to 20k as it was before - green light fixed - other minor fixes --- src/api.py | 14 +++-- src/bitmessagemain.py | 4 ++ src/bitmessageqt/networkstatus.py | 2 +- src/bmconfigparser.py | 2 +- src/class_singleCleaner.py | 9 ++- src/class_singleWorker.py | 42 ++++++++++---- src/network/advanceddispatcher.py | 36 ++++++------ src/network/announcethread.py | 2 +- src/network/asyncore_pollchoose.py | 71 ++++++++++++----------- src/network/bmproto.py | 91 ++++++++++++------------------ src/network/connectionpool.py | 5 +- src/network/invthread.py | 82 +++++++++++++++++++++++++++ src/network/networkthread.py | 2 +- src/network/receivequeuethread.py | 2 +- src/network/stats.py | 49 +++++++++++++++- src/network/tcp.py | 12 +++- src/network/tls.py | 7 ++- src/network/udp.py | 12 ++-- src/protocol.py | 23 +++++--- src/queues.py | 1 + src/state.py | 2 + src/storage/filesystem.py | 4 +- 22 files changed, 316 insertions(+), 158 deletions(-) create mode 100644 src/network/invthread.py diff --git a/src/api.py b/src/api.py index 24c0fc12..f2334484 100644 --- a/src/api.py +++ b/src/api.py @@ -858,8 +858,11 @@ class MySimpleXMLRPCRequestHandler(SimpleXMLRPCRequestHandler): objectType, toStreamNumber, encryptedPayload, int(time.time()) + TTL,'') with shared.printLock: print 'Broadcasting inv for msg(API disseminatePreEncryptedMsg command):', hexlify(inventoryHash) - protocol.broadcastToSendDataQueues(( - toStreamNumber, 'advertiseobject', inventoryHash)) + if BMConfigParser.safeGetBoolean("network", "asyncore"): + queues.invQueue.put((toStreamNumber, inventoryHash)) + else: + protocol.broadcastToSendDataQueues(( + toStreamNumber, 'advertiseobject', inventoryHash)) def HandleTrashSentMessageByAckDAta(self, params): # This API method should only be used when msgid is not available @@ -905,8 +908,11 @@ class MySimpleXMLRPCRequestHandler(SimpleXMLRPCRequestHandler): objectType, pubkeyStreamNumber, payload, int(time.time()) + TTL,'') with shared.printLock: print 'broadcasting inv within API command disseminatePubkey with hash:', hexlify(inventoryHash) - protocol.broadcastToSendDataQueues(( - pubkeyStreamNumber, 'advertiseobject', inventoryHash)) + if BMConfigParser.safeGetBoolean("network", "asyncore"): + queues.invQueue.put((pubkeyStreamNumber, inventoryHash)) + else: + protocol.broadcastToSendDataQueues(( + pubkeyStreamNumber, 'advertiseobject', inventoryHash)) def HandleGetMessageDataByDestinationHash(self, params): # Method will eventually be used by a particular Android app to diff --git a/src/bitmessagemain.py b/src/bitmessagemain.py index d566852c..ca578c43 100755 --- a/src/bitmessagemain.py +++ b/src/bitmessagemain.py @@ -57,6 +57,7 @@ from network.connectionpool import BMConnectionPool from network.networkthread import BMNetworkThread from network.receivequeuethread import ReceiveQueueThread from network.announcethread import AnnounceThread +from network.invthread import InvThread #from network.downloadthread import DownloadThread # Helper Functions @@ -275,6 +276,9 @@ class Main: announceThread = AnnounceThread() announceThread.daemon = True announceThread.start() + state.invThread = InvThread() + state.invThread.daemon = True + state.invThread.start() connectToStream(1) diff --git a/src/bitmessageqt/networkstatus.py b/src/bitmessageqt/networkstatus.py index 158f02fa..ef9e4c42 100644 --- a/src/bitmessageqt/networkstatus.py +++ b/src/bitmessageqt/networkstatus.py @@ -46,7 +46,7 @@ class NetworkStatus(QtGui.QWidget, RetranslateMixin): return "%4.0f kB" % num def updateNumberOfObjectsToBeSynced(self): - self.labelSyncStatus.setText(_translate("networkstatus", "Object(s) to be synced: %n", None, QtCore.QCoreApplication.CodecForTr, PendingDownloadQueue.totalSize() + PendingUpload().len())) + self.labelSyncStatus.setText(_translate("networkstatus", "Object(s) to be synced: %n", None, QtCore.QCoreApplication.CodecForTr, network.stats.pendingDownload() + network.stats.pendingUpload())) def updateNumberOfMessagesProcessed(self): self.updateNumberOfObjectsToBeSynced() diff --git a/src/bmconfigparser.py b/src/bmconfigparser.py index 8c4d8b3c..b727ad65 100644 --- a/src/bmconfigparser.py +++ b/src/bmconfigparser.py @@ -16,7 +16,7 @@ BMConfigDefaults = { "maxuploadrate": 0, }, "network": { - "asyncore": False, + "asyncore": True, "bind": None, }, "inventory": { diff --git a/src/class_singleCleaner.py b/src/class_singleCleaner.py index 6c0a62f6..22b83079 100644 --- a/src/class_singleCleaner.py +++ b/src/class_singleCleaner.py @@ -1,4 +1,5 @@ import threading +import resource import shared import time import sys @@ -9,6 +10,7 @@ from bmconfigparser import BMConfigParser from helper_sql import * from helper_threading import * from inventory import Inventory +from network.connectionpool import BMConnectionPool from debug import logger import knownnodes import queues @@ -36,6 +38,7 @@ resends msg messages in 5 days (then 10 days, then 20 days, etc...) class singleCleaner(threading.Thread, StoppableThread): + cycleLength = 300 def __init__(self): threading.Thread.__init__(self, name="singleCleaner") @@ -51,7 +54,7 @@ class singleCleaner(threading.Thread, StoppableThread): # initial wait if state.shutdown == 0: - self.stop.wait(300) + self.stop.wait(singleCleaner.cycleLength) while state.shutdown == 0: queues.UISignalQueue.put(( @@ -119,8 +122,10 @@ class singleCleaner(threading.Thread, StoppableThread): # TODO: cleanup pending upload / download + logger.info("Memory usage %s (kB)", resource.getrusage(resource.RUSAGE_SELF).ru_maxrss) + if state.shutdown == 0: - self.stop.wait(300) + self.stop.wait(singleCleaner.cycleLength) def resendPubkeyRequest(address): diff --git a/src/class_singleWorker.py b/src/class_singleWorker.py index c2d16de4..ff357b70 100644 --- a/src/class_singleWorker.py +++ b/src/class_singleWorker.py @@ -192,8 +192,11 @@ class singleWorker(threading.Thread, StoppableThread): logger.info('broadcasting inv with hash: ' + hexlify(inventoryHash)) - protocol.broadcastToSendDataQueues(( - streamNumber, 'advertiseobject', inventoryHash)) + if BMConfigParser.safeGetBoolean("network", "asyncore"): + queues.invQueue.put((streamNumber, inventoryHash)) + else: + protocol.broadcastToSendDataQueues(( + streamNumber, 'advertiseobject', inventoryHash)) queues.UISignalQueue.put(('updateStatusBar', '')) try: BMConfigParser().set( @@ -283,8 +286,11 @@ class singleWorker(threading.Thread, StoppableThread): logger.info('broadcasting inv with hash: ' + hexlify(inventoryHash)) - protocol.broadcastToSendDataQueues(( - streamNumber, 'advertiseobject', inventoryHash)) + if BMConfigParser.safeGetBoolean("network", "asyncore"): + queues.invQueue.put((streamNumber, inventoryHash)) + else: + protocol.broadcastToSendDataQueues(( + streamNumber, 'advertiseobject', inventoryHash)) queues.UISignalQueue.put(('updateStatusBar', '')) try: BMConfigParser().set( @@ -374,8 +380,11 @@ class singleWorker(threading.Thread, StoppableThread): logger.info('broadcasting inv with hash: ' + hexlify(inventoryHash)) - protocol.broadcastToSendDataQueues(( - streamNumber, 'advertiseobject', inventoryHash)) + if BMConfigParser.safeGetBoolean("network", "asyncore"): + queues.invQueue.put((streamNumber, inventoryHash)) + else: + protocol.broadcastToSendDataQueues(( + streamNumber, 'advertiseobject', inventoryHash)) queues.UISignalQueue.put(('updateStatusBar', '')) try: BMConfigParser().set( @@ -504,8 +513,11 @@ class singleWorker(threading.Thread, StoppableThread): objectType, streamNumber, payload, embeddedTime, tag) PendingUpload().add(inventoryHash) logger.info('sending inv (within sendBroadcast function) for object: ' + hexlify(inventoryHash)) - protocol.broadcastToSendDataQueues(( - streamNumber, 'advertiseobject', inventoryHash)) + if BMConfigParser.safeGetBoolean("network", "asyncore"): + queues.invQueue.put((streamNumber, inventoryHash)) + else: + protocol.broadcastToSendDataQueues(( + streamNumber, 'advertiseobject', inventoryHash)) queues.UISignalQueue.put(('updateSentItemStatusByAckdata', (ackdata, tr._translate("MainWindow", "Broadcast sent on %1").arg(l10n.formatTimestamp())))) @@ -834,8 +846,11 @@ class singleWorker(threading.Thread, StoppableThread): # not sending to a chan or one of my addresses queues.UISignalQueue.put(('updateSentItemStatusByAckdata', (ackdata, tr._translate("MainWindow", "Message sent. Waiting for acknowledgement. Sent on %1").arg(l10n.formatTimestamp())))) logger.info('Broadcasting inv for my msg(within sendmsg function):' + hexlify(inventoryHash)) - protocol.broadcastToSendDataQueues(( - toStreamNumber, 'advertiseobject', inventoryHash)) + if BMConfigParser.safeGetBoolean("network", "asyncore"): + queues.invQueue.put((toStreamNumber, inventoryHash)) + else: + protocol.broadcastToSendDataQueues(( + toStreamNumber, 'advertiseobject', inventoryHash)) # Update the sent message in the sent table with the necessary information. if BMConfigParser().has_section(toaddress) or not protocol.checkBitfield(behaviorBitfield, protocol.BITFIELD_DOESACK): @@ -937,8 +952,11 @@ class singleWorker(threading.Thread, StoppableThread): objectType, streamNumber, payload, embeddedTime, '') PendingUpload().add(inventoryHash) logger.info('sending inv (for the getpubkey message)') - protocol.broadcastToSendDataQueues(( - streamNumber, 'advertiseobject', inventoryHash)) + if BMConfigParser.safeGetBoolean("network", "asyncore"): + queues.invQueue.put((streamNumber, inventoryHash)) + else: + protocol.broadcastToSendDataQueues(( + streamNumber, 'advertiseobject', inventoryHash)) # wait 10% past expiration sleeptill = int(time.time() + TTL * 1.1) diff --git a/src/network/advanceddispatcher.py b/src/network/advanceddispatcher.py index 96b206cf..5f8a94aa 100644 --- a/src/network/advanceddispatcher.py +++ b/src/network/advanceddispatcher.py @@ -2,6 +2,7 @@ import Queue import time import asyncore_pollchoose as asyncore +from debug import logger from bmconfigparser import BMConfigParser class AdvancedDispatcher(asyncore.dispatcher): @@ -56,44 +57,45 @@ class AdvancedDispatcher(asyncore.dispatcher): self.state = state def writable(self): - return self.connecting or len(self.write_buf) > 0 or not self.writeQueue.empty() + return asyncore.dispatcher.writable(self) and \ + (self.connecting or len(self.write_buf) > 0 or not self.writeQueue.empty()) def readable(self): - return self.connecting or len(self.read_buf) < AdvancedDispatcher._buf_len + return asyncore.dispatcher.readable(self) and \ + (self.connecting or len(self.read_buf) < AdvancedDispatcher._buf_len) def handle_read(self): self.lastTx = time.time() downloadBytes = AdvancedDispatcher._buf_len if asyncore.maxDownloadRate > 0: - downloadBytes = asyncore.downloadChunk + downloadBytes = asyncore.downloadBucket if self.expectBytes > 0 and downloadBytes > self.expectBytes: downloadBytes = self.expectBytes - newData = self.recv(downloadBytes) - if asyncore.maxDownloadRate > 0: - asyncore.downloadBucket -= len(newData) - self.receivedBytes += len(newData) - if self.expectBytes > 0: - self.expectBytes -= len(newData) - asyncore.updateReceived(len(newData)) - self.read_buf += newData + if downloadBytes > 0: + newData = self.recv(downloadBytes) + self.receivedBytes += len(newData) + if self.expectBytes > 0: + self.expectBytes -= len(newData) + asyncore.update_received(len(newData)) + self.read_buf += newData self.process() def handle_write(self): self.lastTx = time.time() + bufSize = AdvancedDispatcher._buf_len if asyncore.maxUploadRate > 0: - bufSize = asyncore.uploadChunk - else: - bufSize = self._buf_len + bufSize = asyncore.uploadBucket while len(self.write_buf) < bufSize: try: self.write_buf += self.writeQueue.get(False) self.writeQueue.task_done() except Queue.Empty: break + if bufSize <= 0: + return if len(self.write_buf) > 0: written = self.send(self.write_buf[0:bufSize]) - asyncore.uploadBucket -= written - asyncore.updateSent(written) + asyncore.update_sent(written) self.sentBytes += written self.slice_write_buf(written) @@ -107,7 +109,7 @@ class AdvancedDispatcher(asyncore.dispatcher): def close(self): self.read_buf = b"" self.write_buf = b"" - self.state = "shutdown" + self.state = "close" while True: try: self.writeQueue.get(False) diff --git a/src/network/announcethread.py b/src/network/announcethread.py index 0ba93d7a..29ed301e 100644 --- a/src/network/announcethread.py +++ b/src/network/announcethread.py @@ -17,7 +17,7 @@ class AnnounceThread(threading.Thread, StoppableThread): self.initStop() self.name = "AnnounceThread" BMConnectionPool() - logger.error("init announce thread") + logger.info("init announce thread") def run(self): lastSelfAnnounced = 0 diff --git a/src/network/asyncore_pollchoose.py b/src/network/asyncore_pollchoose.py index dda6d7c2..25a5b3fb 100644 --- a/src/network/asyncore_pollchoose.py +++ b/src/network/asyncore_pollchoose.py @@ -90,12 +90,10 @@ class ExitNow(Exception): _reraised_exceptions = (ExitNow, KeyboardInterrupt, SystemExit) maxDownloadRate = 0 -downloadChunk = 0 downloadTimestamp = 0 downloadBucket = 0 receivedBytes = 0 maxUploadRate = 0 -uploadChunk = 0 uploadTimestamp = 0 uploadBucket = 0 sentBytes = 0 @@ -117,48 +115,37 @@ def write(obj): obj.handle_error() def set_rates(download, upload): - global maxDownloadRate, maxUploadRate, downloadChunk, uploadChunk, downloadBucket, uploadBucket, downloadTimestamp, uploadTimestamp + global maxDownloadRate, maxUploadRate, downloadBucket, uploadBucket, downloadTimestamp, uploadTimestamp maxDownloadRate = float(download) - if maxDownloadRate > 0: - downloadChunk = 1400 maxUploadRate = float(upload) - if maxUploadRate > 0: - uploadChunk = 1400 downloadBucket = maxDownloadRate uploadBucket = maxUploadRate downloadTimestamp = time.time() uploadTimestamp = time.time() -def updateReceived(download=0): - global receivedBytes +def update_received(download=0): + global receivedBytes, maxDownloadRate, downloadBucket, downloadTimestamp + currentTimestamp = time.time() receivedBytes += download + if maxDownloadRate > 0: + bucketIncrease = int(maxDownloadRate * (currentTimestamp - downloadTimestamp)) + downloadBucket += bucketIncrease + if downloadBucket > maxDownloadRate: + downloadBucket = int(maxDownloadRate) + downloadBucket -= download + downloadTimestamp = currentTimestamp -def updateSent(upload=0): - global sentBytes +def update_sent(upload=0): + global sentBytes, maxUploadRate, uploadBucket, uploadTimestamp + currentTimestamp = time.time() sentBytes += upload - -def wait_tx_buckets(): - global downloadBucket, uploadBucket, downloadTimestamp, uploadTimestamp - if maxDownloadRate > 0 and maxUploadRate > 0: - wait_for_this_long = min(maxDownloadRate / downloadChunk, maxUploadRate / uploadChunk) - elif maxDownloadRate > 0: - wait_for_this_long = maxDownloadRate / downloadChunk - elif maxUploadRate > 0: - wait_for_this_long = maxUploadRate / uploadChunk - else: - return - wait_for_this_long /= 2 - if wait_for_this_long > 1: - wait_for_this_long = 1 - elif wait_for_this_long < 0.1: - wait_for_this_long = 0.1 - - while downloadBucket < downloadChunk and uploadBucket < uploadChunk: - time.sleep(wait_for_this_long) - downloadBucket += (time.time() - downloadTimestamp) * maxDownloadRate - downloadTimestamp = time.time() - uploadBucket += (time.time() - uploadTimestamp) * maxUploadRate - uploadTimestamp = time.time() + if maxUploadRate > 0: + bucketIncrease = int(maxUploadRate * (currentTimestamp - uploadTimestamp)) + uploadBucket += bucketIncrease + if uploadBucket > maxUploadRate: + uploadBucket = int(maxUploadRate) + uploadBucket -= upload + uploadTimestamp = currentTimestamp def _exception(obj): try: @@ -376,13 +363,19 @@ def loop(timeout=30.0, use_poll=False, map=None, count=None, if count is None: while map: - wait_tx_buckets() + # fill buckets first + update_sent() + update_received() + # then poll poller(timeout, map) else: timeout /= count while map and count > 0: - wait_tx_buckets() + # fill buckets first + update_sent() + update_received() poller(timeout, map) + # then poll count = count - 1 class dispatcher: @@ -396,6 +389,8 @@ class dispatcher: ignore_log_types = frozenset(['warning']) poller_registered = False flags = 0 + # don't do network IO with a smaller bucket than this + minTx = 1500 def __init__(self, sock=None, map=None): if map is None: @@ -499,9 +494,13 @@ class dispatcher: # ================================================== def readable(self): + if maxDownloadRate > 0: + return downloadBucket > dispatcher.minTx return True def writable(self): + if maxUploadRate > 0: + return uploadBucket > dispatcher.minTx return True # ================================================== diff --git a/src/network/bmproto.py b/src/network/bmproto.py index d3f02568..a99bdfb2 100644 --- a/src/network/bmproto.py +++ b/src/network/bmproto.py @@ -3,7 +3,6 @@ from binascii import hexlify import hashlib import math import time -from pprint import pprint import socket import struct import random @@ -25,7 +24,7 @@ from network.uploadqueue import UploadQueue, UploadElem, AddrUploadQueue, ObjUpl import addresses from bmconfigparser import BMConfigParser -from queues import objectProcessorQueue, portCheckerQueue, UISignalQueue +from queues import objectProcessorQueue, portCheckerQueue, UISignalQueue, invQueue import shared import state import protocol @@ -53,35 +52,11 @@ class BMProto(AdvancedDispatcher, ObjectTracker): # maximum time offset maxTimeOffset = 3600 -# def __init__(self, address=None, sock=None): -# AdvancedDispatcher.__init__(self, sock) -# self.verackReceived = False -# self.verackSent = False -# self.lastTx = time.time() -# self.streams = [0] -# self.fullyEstablished = False -# self.connectedAt = 0 -# self.skipUntil = 0 -# if address is None and sock is not None: -# self.destination = state.Peer(sock.getpeername()[0], sock.getpeername()[1]) -# self.isOutbound = False -# TLSDispatcher.__init__(self, sock, server_side=True) -# self.connectedAt = time.time() -# #print "received connection in background from %s:%i" % (self.destination.host, self.destination.port) -# else: -# self.destination = address -# self.isOutbound = True -# if ":" in address.host: -# self.create_socket(socket.AF_INET6, socket.SOCK_STREAM) -# else: -# self.create_socket(socket.AF_INET, socket.SOCK_STREAM) -# self.socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) -# TLSDispatcher.__init__(self, sock, server_side=False) -# self.connect(self.destination) -# #print "connecting in background to %s:%i" % (self.destination.host, self.destination.port) -# shared.connectedHostsList[self.destination] = 0 -# ObjectTracker.__init__(self) -# UISignalQueue.put(('updateNetworkStatusTab', 'no data')) + def __init__(self, address=None, sock=None): + AdvancedDispatcher.__init__(self, sock) + self.isOutbound = False + # packet/connection from a local IP + self.local = False def bm_proto_reset(self): self.magic = None @@ -95,7 +70,6 @@ class BMProto(AdvancedDispatcher, ObjectTracker): self.object = None def state_bm_header(self): - #print "%s:%i: header" % (self.destination.host, self.destination.port) if len(self.read_buf) < protocol.Header.size: #print "Length below header size" return False @@ -105,7 +79,7 @@ class BMProto(AdvancedDispatcher, ObjectTracker): # skip 1 byte in order to sync self.bm_proto_reset() self.set_state("bm_header", 1) - print "Bad magic" + logger.debug("Bad magic") self.close() return False if self.payloadLength > BMProto.maxMessageSize: @@ -117,10 +91,10 @@ class BMProto(AdvancedDispatcher, ObjectTracker): if len(self.read_buf) < self.payloadLength: #print "Length below announced object length" return False - print "%s:%i: command %s (%ib)" % (self.destination.host, self.destination.port, self.command, self.payloadLength) + #logger.debug("%s:%i: command %s (%ib)", self.destination.host, self.destination.port, self.command, self.payloadLength) self.payload = self.read_buf[:self.payloadLength] if self.checksum != hashlib.sha512(self.payload).digest()[0:4]: - print "Bad checksum, ignoring" + logger.debug("Bad checksum, ignoring") self.invalid = True retval = True if not self.fullyEstablished and self.command not in ("version", "verack"): @@ -131,28 +105,28 @@ class BMProto(AdvancedDispatcher, ObjectTracker): retval = getattr(self, "bm_command_" + str(self.command).lower())() except AttributeError: # unimplemented command - print "unimplemented command %s" % (self.command) + logger.debug("unimplemented command %s", self.command) except BMProtoInsufficientDataError: - print "packet length too short, skipping" + logger.debug("packet length too short, skipping") except BMProtoExcessiveDataError: - print "too much data, skipping" + logger.debug("too much data, skipping") except BMObjectInsufficientPOWError: - print "insufficient PoW, skipping" + logger.debug("insufficient PoW, skipping") except BMObjectInvalidDataError: - print "object invalid data, skipping" + logger.debug("object invalid data, skipping") except BMObjectExpiredError: - print "object expired, skipping" + logger.debug("object expired, skipping") except BMObjectUnwantedStreamError: - print "object not in wanted stream, skipping" + logger.debug("object not in wanted stream, skipping") except BMObjectInvalidError: - print "object invalid, skipping" + logger.debug("object invalid, skipping") except BMObjectAlreadyHaveError: - print "already got object, skipping" + logger.debug("already got object, skipping") except struct.error: - print "decoding error, skipping" + logger.debug("decoding error, skipping") else: #print "Skipping command %s due to invalid data" % (self.command) - print "Closing due to invalid data" % (self.command) + logger.debug("Closing due to invalid command %s", self.command) self.close() return False if retval: @@ -253,13 +227,13 @@ class BMProto(AdvancedDispatcher, ObjectTracker): self.payloadOffset += 8 i += 1 if self.payloadOffset > self.payloadLength: - print "Insufficient data %i/%i" % (self.payloadOffset, self.payloadLength) + logger.debug("Insufficient data %i/%i", self.payloadOffset, self.payloadLength) raise BMProtoInsufficientDataError() return retval def bm_command_error(self): fatalStatus, banTime, inventoryVector, errorText = self.decode_payload_content("vvlsls") - print "%s:%i error: %i, %s" % (self.destination.host, self.destination.port, fatalStatus, errorText) + logger.error("%s:%i error: %i, %s", self.destination.host, self.destination.port, fatalStatus, errorText) return True def bm_command_getdata(self): @@ -325,6 +299,7 @@ class BMProto(AdvancedDispatcher, ObjectTracker): objectProcessorQueue.put((self.object.objectType,self.object.data)) #DownloadQueue().task_done(self.object.inventoryHash) network.connectionpool.BMConnectionPool().handleReceivedObject(self, self.object.streamNumber, self.object.inventoryHash) + invQueue.put((self.object.streamNumber, self.object.inventoryHash)) #ObjUploadQueue().put(UploadElem(self.object.streamNumber, self.object.inventoryHash)) #broadcastToSendDataQueues((streamNumber, 'advertiseobject', inventoryHash)) return True @@ -344,8 +319,11 @@ class BMProto(AdvancedDispatcher, ObjectTracker): peer = state.Peer(decodedIP, port) if peer in knownnodes.knownNodes[stream] and knownnodes.knownNodes[stream][peer] > seenTime: continue - knownnodes.knownNodes[stream][peer] = seenTime - AddrUploadQueue().put((stream, peer)) + if len(knownnodes.knownNodes[stream]) < 20000: + with knownnodes.knownNodesLock: + knownnodes.knownNodes[stream][peer] = seenTime + #knownnodes.knownNodes[stream][peer] = seenTime + #AddrUploadQueue().put((stream, peer)) return True def bm_command_portcheck(self): @@ -392,7 +370,7 @@ class BMProto(AdvancedDispatcher, ObjectTracker): self.verackSent = True if not self.isOutbound: self.writeQueue.put(protocol.assembleVersionMessage(self.destination.host, self.destination.port, network.connectionpool.BMConnectionPool().streams, True)) - print "%s:%i: Sending version" % (self.destination.host, self.destination.port) + #print "%s:%i: Sending version" % (self.destination.host, self.destination.port) if ((self.services & protocol.NODE_SSL == protocol.NODE_SSL) and protocol.haveSSL(not self.isOutbound)): self.isSSL = True @@ -472,10 +450,11 @@ class BMProto(AdvancedDispatcher, ObjectTracker): def close(self, reason=None): self.set_state("close") -# if reason is None: -# print "%s:%i: closing" % (self.destination.host, self.destination.port) -# #traceback.print_stack() -# else: -# print "%s:%i: closing, %s" % (self.destination.host, self.destination.port, reason) + if reason is None: + #logger.debug("%s:%i: closing, %s", self.destination.host, self.destination.port, ''.join(traceback.format_stack())) + logger.debug("%s:%i: closing", self.destination.host, self.destination.port) + #traceback.print_stack() + else: + logger.debug("%s:%i: closing, %s", self.destination.host, self.destination.port, reason) network.connectionpool.BMConnectionPool().removeConnection(self) AdvancedDispatcher.close(self) diff --git a/src/network/connectionpool.py b/src/network/connectionpool.py index a75b62aa..d1a6b6ee 100644 --- a/src/network/connectionpool.py +++ b/src/network/connectionpool.py @@ -21,8 +21,8 @@ import state class BMConnectionPool(object): def __init__(self): asyncore.set_rates( - BMConfigParser().safeGetInt("bitmessagesettings", "maxdownloadrate"), - BMConfigParser().safeGetInt("bitmessagesettings", "maxuploadrate")) + BMConfigParser().safeGetInt("bitmessagesettings", "maxdownloadrate") * 1024, + BMConfigParser().safeGetInt("bitmessagesettings", "maxuploadrate") * 1024) self.outboundConnections = {} self.inboundConnections = {} self.listeningSockets = {} @@ -117,7 +117,6 @@ class BMConnectionPool(object): if spawnConnections: if not self.bootstrapped: - print "bootstrapping dns" helper_bootstrap.dns() self.bootstrapped = True established = sum(1 for c in self.outboundConnections.values() if (c.connected and c.fullyEstablished)) diff --git a/src/network/invthread.py b/src/network/invthread.py new file mode 100644 index 00000000..75d53a20 --- /dev/null +++ b/src/network/invthread.py @@ -0,0 +1,82 @@ +import collections +import Queue +import random +import threading +import time + +import addresses +from bmconfigparser import BMConfigParser +from debug import logger +from helper_threading import StoppableThread +from network.bmproto import BMProto +from network.connectionpool import BMConnectionPool +from queues import invQueue +import protocol +import state + +class InvThread(threading.Thread, StoppableThread): + size = 10 + + def __init__(self): + threading.Thread.__init__(self, name="InvThread") + self.initStop() + self.name = "InvThread" + + self.shutdown = False + + self.collectionOfInvs = [] + for i in range(InvThread.size): + self.collectionOfInvs.append({}) + + def run(self): + iterator = 0 + while not state.shutdown: + while True: + try: + (stream, hash) = invQueue.get(False) + self.holdHash (stream, hash) + except Queue.Empty: + break + + if len(self.collectionOfInvs[iterator]) > 0: + for connection in BMConnectionPool().inboundConnections.values() + BMConnectionPool().outboundConnections.values(): + hashes = [] + for stream in connection.streams: + try: + for hashId in self.collectionOfInvs[iterator][stream]: + if hashId in connection.objectsNewToThem: + hashes.append(hashId) + del connection.objectsNewToThem[hashId] + except KeyError: + continue + if len(hashes) > 0: + connection.writeQueue.put(protocol.CreatePacket('inv', addresses.encodeVarint(len(hashes)) + b"".join(hashes))) + self.collectionOfInvs[iterator] = [] + iterator += 1 + iterator %= InvThread.size + self.stop.wait(1) + + def holdHash(self, stream, hash): + iter = random.randrange(0, InvThread.size) + try: + self.collectionOfInvs[iter][stream].append(hash) + except KeyError, IndexError: + self.collectionOfInvs[iter][stream] = [] + self.collectionOfInvs[iter][stream].append(hash) + + def hasHash(self, hash): + for streamlist in self.collectionOfInvs: + for stream in streamlist: + if hash in streamlist[stream]: + return True + return False + + def hashCount(self): + retval = 0 + for streamlist in self.collectionOfInvs: + for stream in streamlist: + retval += len(streamlist[stream]) + return retval + + def close(self): + self.shutdown = True diff --git a/src/network/networkthread.py b/src/network/networkthread.py index 498bd340..bb6c0301 100644 --- a/src/network/networkthread.py +++ b/src/network/networkthread.py @@ -12,7 +12,7 @@ class BMNetworkThread(threading.Thread, StoppableThread): self.initStop() self.name = "AsyncoreThread" BMConnectionPool() - logger.error("init asyncore thread") + logger.info("init asyncore thread") def run(self): while not self._stopped: diff --git a/src/network/receivequeuethread.py b/src/network/receivequeuethread.py index c5509b65..a0a2c4b8 100644 --- a/src/network/receivequeuethread.py +++ b/src/network/receivequeuethread.py @@ -17,7 +17,7 @@ class ReceiveQueueThread(threading.Thread, StoppableThread): self.initStop() self.name = "ReceiveQueueThread" BMConnectionPool() - logger.error("init receive queue thread") + logger.info("init receive queue thread") def run(self): lastprinted = int(time.time()) diff --git a/src/network/stats.py b/src/network/stats.py index 838ef23a..36baf2cb 100644 --- a/src/network/stats.py +++ b/src/network/stats.py @@ -1,9 +1,19 @@ +import time + from bmconfigparser import BMConfigParser from network.connectionpool import BMConnectionPool +from inventory import PendingDownloadQueue, PendingUpload import asyncore_pollchoose as asyncore import shared import throttle +lastReceivedTimestamp = time.time() +lastReceivedBytes = 0 +currentReceivedSpeed = 0 +lastSentTimestamp = time.time() +lastSentBytes = 0 +currentSentSpeed = 0 + def connectedHostsList(): if BMConfigParser().safeGetBoolean("network", "asyncore"): retval = [] @@ -25,8 +35,15 @@ def sentBytes(): return throttle.SendThrottle().total def uploadSpeed(): + global lastSentTimestamp, lastSentBytes, currentSentSpeed if BMConfigParser().safeGetBoolean("network", "asyncore"): - return 0 + currentTimestamp = time.time() + if int(lastSentTimestamp) < int(currentTimestamp): + currentSentBytes = asyncore.sentBytes + currentSentSpeed = int((currentSentBytes - lastSentBytes) / (currentTimestamp - lastSentTimestamp)) + lastSentBytes = currentSentBytes + lastSentTimestamp = currentTimestamp + return currentSentSpeed else: return throttle.sendThrottle().getSpeed() @@ -37,7 +54,35 @@ def receivedBytes(): return throttle.ReceiveThrottle().total def downloadSpeed(): + global lastReceivedTimestamp, lastReceivedBytes, currentReceivedSpeed if BMConfigParser().safeGetBoolean("network", "asyncore"): - return 0 + currentTimestamp = time.time() + if int(lastReceivedTimestamp) < int(currentTimestamp): + currentReceivedBytes = asyncore.receivedBytes + currentReceivedSpeed = int((currentReceivedBytes - lastReceivedBytes) / (currentTimestamp - lastReceivedTimestamp)) + lastReceivedBytes = currentReceivedBytes + lastReceivedTimestamp = currentTimestamp + return currentReceivedSpeed else: return throttle.ReceiveThrottle().getSpeed() + +def pendingDownload(): + if BMConfigParser().safeGetBoolean("network", "asyncore"): + tmp = {} + for connection in BMConnectionPool().inboundConnections.values() + BMConnectionPool().outboundConnections.values(): + for k in connection.objectsNewToMe.keys(): + tmp[k] = True + return len(tmp) + else: + return PendingDownloadQueue.totalSize() + +def pendingUpload(): + if BMConfigParser().safeGetBoolean("network", "asyncore"): + return 0 + tmp = {} + for connection in BMConnectionPool().inboundConnections.values() + BMConnectionPool().outboundConnections.values(): + for k in connection.objectsNewToThem.keys(): + tmp[k] = True + return len(tmp) + else: + return PendingUpload().len() diff --git a/src/network/tcp.py b/src/network/tcp.py index 8c5fb968..42d5a831 100644 --- a/src/network/tcp.py +++ b/src/network/tcp.py @@ -36,7 +36,7 @@ import protocol class TCPConnection(BMProto, TLSDispatcher): def __init__(self, address=None, sock=None): - AdvancedDispatcher.__init__(self, sock) + BMProto.__init__(self, address=address, sock=sock) self.verackReceived = False self.verackSent = False self.streams = [0] @@ -60,7 +60,12 @@ class TCPConnection(BMProto, TLSDispatcher): TLSDispatcher.__init__(self, sock, server_side=False) self.connect(self.destination) logger.debug("Connecting to %s:%i", self.destination.host, self.destination.port) - shared.connectedHostsList[self.destination] = 0 + encodedAddr = protocol.encodeHost(self.destination.host) + if protocol.checkIPAddress(encodedAddr, True) and not protocol.checkSocksIP(self.destination.host): + self.local = True + else: + self.local = False + #shared.connectedHostsList[self.destination] = 0 ObjectTracker.__init__(self) UISignalQueue.put(('updateNetworkStatusTab', 'no data')) self.bm_proto_reset() @@ -83,6 +88,9 @@ class TCPConnection(BMProto, TLSDispatcher): self.skipUntil = time.time() + delay def set_connection_fully_established(self): + if not self.isOutbound and not self.local: + shared.clientHasReceivedIncomingConnections = True + UISignalQueue.put(('setStatusIcon', 'green')) UISignalQueue.put(('updateNetworkStatusTab', 'no data')) self.antiIntersectionDelay(True) self.fullyEstablished = True diff --git a/src/network/tls.py b/src/network/tls.py index f813e3be..115f3faa 100644 --- a/src/network/tls.py +++ b/src/network/tls.py @@ -7,6 +7,7 @@ import socket import ssl import sys +from debug import logger from network.advanceddispatcher import AdvancedDispatcher import network.asyncore_pollchoose as asyncore import paths @@ -108,10 +109,10 @@ class TLSDispatcher(AdvancedDispatcher): return False # Perform the handshake. try: - print "handshaking (internal)" + #print "handshaking (internal)" self.sslSocket.do_handshake() except ssl.SSLError, err: - print "%s:%i: handshake fail" % (self.destination.host, self.destination.port) + #print "%s:%i: handshake fail" % (self.destination.host, self.destination.port) self.want_read = self.want_write = False if err.args[0] == ssl.SSL_ERROR_WANT_READ: #print "want read" @@ -122,7 +123,7 @@ class TLSDispatcher(AdvancedDispatcher): if not (self.want_write or self.want_read): raise else: - print "%s:%i: TLS handshake success%s" % (self.destination.host, self.destination.port, ", TLS protocol version: %s" % (self.sslSocket.version()) if sys.version_info >= (2, 7, 9) else "") + logger.debug("%s:%i: TLS handshake success%s", self.destination.host, self.destination.port, ", TLS protocol version: %s" % (self.sslSocket.version()) if sys.version_info >= (2, 7, 9) else "") # The handshake has completed, so remove this channel and... self.del_channel() self.set_socket(self.sslSocket) diff --git a/src/network/udp.py b/src/network/udp.py index 9e687603..29e434a2 100644 --- a/src/network/udp.py +++ b/src/network/udp.py @@ -35,7 +35,7 @@ class UDPSocket(BMProto): announceInterval = 60 def __init__(self, host=None, sock=None): - AdvancedDispatcher.__init__(self, sock) + BMProto.__init__(self, sock) self.verackReceived = True self.verackSent = True # TODO sort out streams @@ -43,7 +43,6 @@ class UDPSocket(BMProto): self.fullyEstablished = True self.connectedAt = 0 self.skipUntil = 0 - self.isOutbound = False if sock is None: if host is None: host = '' @@ -51,7 +50,7 @@ class UDPSocket(BMProto): self.create_socket(socket.AF_INET6, socket.SOCK_DGRAM) else: self.create_socket(socket.AF_INET, socket.SOCK_DGRAM) - print "binding to %s" % (host) + logger.info("Binding UDP socket to %s:%i", host, UDPSocket.port) self.socket.bind((host, UDPSocket.port)) #BINDTODEVICE is only available on linux and requires root #try: @@ -67,10 +66,11 @@ class UDPSocket(BMProto): ObjectTracker.__init__(self) self.connecting = False self.connected = True - # packet was received from a local IP - self.local = False self.set_state("bm_header") + def state_bm_command(self): + BMProto.state_bm_command(self) + # disable most commands before doing research / testing # only addr (peer discovery), error and object are implemented @@ -163,7 +163,7 @@ class UDPSocket(BMProto): return try: retval = self.socket.sendto(data, ('', UDPSocket.port)) - print "broadcasted %ib" % (retval) + #print "broadcasted %ib" % (retval) except socket.error as e: print "socket error on sendato: %s" % (e) self.writeQueue.task_done() diff --git a/src/protocol.py b/src/protocol.py index 5661dff0..d7bd5b8c 100644 --- a/src/protocol.py +++ b/src/protocol.py @@ -106,28 +106,35 @@ def checkIPAddress(host, private=False): def checkIPv4Address(host, hostStandardFormat, private=False): if host[0] == '\x7F': # 127/8 - logger.debug('Ignoring IP address in loopback range: ' + hostStandardFormat) + if not private: + logger.debug('Ignoring IP address in loopback range: ' + hostStandardFormat) return False if host[0] == '\x0A': # 10/8 - logger.debug('Ignoring IP address in private range: ' + hostStandardFormat) + if not private: + logger.debug('Ignoring IP address in private range: ' + hostStandardFormat) return hostStandardFormat if private else False if host[0:2] == '\xC0\xA8': # 192.168/16 - logger.debug('Ignoring IP address in private range: ' + hostStandardFormat) + if not private: + logger.debug('Ignoring IP address in private range: ' + hostStandardFormat) return hostStandardFormat if private else False if host[0:2] >= '\xAC\x10' and host[0:2] < '\xAC\x20': # 172.16/12 - logger.debug('Ignoring IP address in private range:' + hostStandardFormat) - return False + if not private: + logger.debug('Ignoring IP address in private range:' + hostStandardFormat) + return hostStandardFormat if private else False return False if private else hostStandardFormat def checkIPv6Address(host, hostStandardFormat, private=False): if host == ('\x00' * 15) + '\x01': - logger.debug('Ignoring loopback address: ' + hostStandardFormat) + if not private: + logger.debug('Ignoring loopback address: ' + hostStandardFormat) return False if host[0] == '\xFE' and (ord(host[1]) & 0xc0) == 0x80: - logger.debug ('Ignoring local address: ' + hostStandardFormat) + if not private: + logger.debug ('Ignoring local address: ' + hostStandardFormat) return hostStandardFormat if private else False if (ord(host[0]) & 0xfe) == 0xfc: - logger.debug ('Ignoring unique local address: ' + hostStandardFormat) + if not private: + logger.debug ('Ignoring unique local address: ' + hostStandardFormat) return hostStandardFormat if private else False return False if private else hostStandardFormat diff --git a/src/queues.py b/src/queues.py index a11bedeb..7c36d54a 100644 --- a/src/queues.py +++ b/src/queues.py @@ -6,6 +6,7 @@ UISignalQueue = Queue.Queue() addressGeneratorQueue = Queue.Queue() # receiveDataThreads dump objects they hear on the network into this queue to be processed. objectProcessorQueue = ObjectProcessorQueue() +invQueue = Queue.Queue() portCheckerQueue = Queue.Queue() peerDiscoveryQueue = Queue.Queue() apiAddressGeneratorReturnQueue = Queue.Queue( diff --git a/src/state.py b/src/state.py index 72852f3e..ff8a143d 100644 --- a/src/state.py +++ b/src/state.py @@ -23,6 +23,8 @@ sqlReady = False # set to true by sqlTread when ready for processing maximumNumberOfHalfOpenConnections = 0 +invThread = None + # If the trustedpeer option is specified in keys.dat then this will # contain a Peer which will be connected to instead of using the # addresses advertised by other peers. The client will only connect to diff --git a/src/storage/filesystem.py b/src/storage/filesystem.py index bb4d0e3f..4efe1132 100644 --- a/src/storage/filesystem.py +++ b/src/storage/filesystem.py @@ -111,8 +111,8 @@ class FilesystemInventory(InventoryStorage): print "error loading %s" % (hexlify(hashId)) pass self._inventory = newInventory - for i, v in self._inventory.items(): - print "loaded stream: %s, %i items" % (i, len(v)) +# for i, v in self._inventory.items(): +# print "loaded stream: %s, %i items" % (i, len(v)) def stream_list(self): return self._inventory.keys() From 65bb6648e75f4c5f596e45ab870d9370504a2137 Mon Sep 17 00:00:00 2001 From: Peter Surda Date: Mon, 29 May 2017 00:47:41 +0200 Subject: [PATCH 066/407] Asyncore updates - fix crash in inv thread - more prints changed into logger - minor fixes --- src/network/bmproto.py | 2 +- src/network/invthread.py | 12 +++++------- src/network/networkthread.py | 2 +- src/network/udp.py | 10 +++------- 4 files changed, 10 insertions(+), 16 deletions(-) diff --git a/src/network/bmproto.py b/src/network/bmproto.py index a99bdfb2..eb372aa7 100644 --- a/src/network/bmproto.py +++ b/src/network/bmproto.py @@ -97,7 +97,7 @@ class BMProto(AdvancedDispatcher, ObjectTracker): logger.debug("Bad checksum, ignoring") self.invalid = True retval = True - if not self.fullyEstablished and self.command not in ("version", "verack"): + if not self.fullyEstablished and self.command not in ("error", "version", "verack"): logger.error("Received command %s before connection was fully established, ignoring", self.command) self.invalid = True if not self.invalid: diff --git a/src/network/invthread.py b/src/network/invthread.py index 75d53a20..37fb7094 100644 --- a/src/network/invthread.py +++ b/src/network/invthread.py @@ -51,18 +51,16 @@ class InvThread(threading.Thread, StoppableThread): continue if len(hashes) > 0: connection.writeQueue.put(protocol.CreatePacket('inv', addresses.encodeVarint(len(hashes)) + b"".join(hashes))) - self.collectionOfInvs[iterator] = [] + self.collectionOfInvs[iterator] = {} iterator += 1 iterator %= InvThread.size self.stop.wait(1) def holdHash(self, stream, hash): - iter = random.randrange(0, InvThread.size) - try: - self.collectionOfInvs[iter][stream].append(hash) - except KeyError, IndexError: - self.collectionOfInvs[iter][stream] = [] - self.collectionOfInvs[iter][stream].append(hash) + i = random.randrange(0, InvThread.size) + if stream not in self.collectionOfInvs[i]: + self.collectionOfInvs[i][stream] = [] + self.collectionOfInvs[i][stream].append(hash) def hasHash(self, hash): for streamlist in self.collectionOfInvs: diff --git a/src/network/networkthread.py b/src/network/networkthread.py index bb6c0301..54c58f12 100644 --- a/src/network/networkthread.py +++ b/src/network/networkthread.py @@ -19,7 +19,6 @@ class BMNetworkThread(threading.Thread, StoppableThread): BMConnectionPool().loop() def stopThread(self): - super(BMNetworkThread, self).stopThread() for i in BMConnectionPool().listeningSockets: try: i.close() @@ -38,3 +37,4 @@ class BMNetworkThread(threading.Thread, StoppableThread): # just in case asyncore.close_all() + super(BMNetworkThread, self).stopThread() diff --git a/src/network/udp.py b/src/network/udp.py index 29e434a2..5412083f 100644 --- a/src/network/udp.py +++ b/src/network/udp.py @@ -3,7 +3,6 @@ from binascii import hexlify import hashlib import math import time -from pprint import pprint import socket import struct import random @@ -107,7 +106,7 @@ class UDPSocket(BMProto): remoteport = port if remoteport is False: return - print "received peer discovery from %s:%i (port %i):" % (self.destination.host, self.destination.port, remoteport) + logger.debug("received peer discovery from %s:%i (port %i):", self.destination.host, self.destination.port, remoteport) if self.local: peerDiscoveryQueue.put(state.Peer(self.destination.host, remoteport)) return True @@ -140,7 +139,7 @@ class UDPSocket(BMProto): try: (recdata, addr) = self.socket.recvfrom(AdvancedDispatcher._buf_len) except socket.error as e: - print "socket error: %s" % (str(e)) + logger.error("socket error: %s", str(e)) return self.destination = state.Peer(addr[0], addr[1]) @@ -149,23 +148,20 @@ class UDPSocket(BMProto): self.local = True else: self.local = False - print "read %ib" % (len(recdata)) # overwrite the old buffer to avoid mixing data and so that self.local works correctly self.read_buf = recdata self.bm_proto_reset() self.process() def handle_write(self): -# print "handling write" try: data = self.writeQueue.get(False) except Queue.Empty: return try: retval = self.socket.sendto(data, ('', UDPSocket.port)) - #print "broadcasted %ib" % (retval) except socket.error as e: - print "socket error on sendato: %s" % (e) + logger.error("socket error on sendato: %s", str(e)) self.writeQueue.task_done() From 73c41bff9df6fedd2a31542281920d23041f2e20 Mon Sep 17 00:00:00 2001 From: Peter Surda Date: Mon, 29 May 2017 02:30:18 +0200 Subject: [PATCH 067/407] typo -BMConfigParser. instead of BMConfigParser(). --- src/api.py | 4 ++-- src/class_singleWorker.py | 12 ++++++------ 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/src/api.py b/src/api.py index f2334484..ee371b38 100644 --- a/src/api.py +++ b/src/api.py @@ -858,7 +858,7 @@ class MySimpleXMLRPCRequestHandler(SimpleXMLRPCRequestHandler): objectType, toStreamNumber, encryptedPayload, int(time.time()) + TTL,'') with shared.printLock: print 'Broadcasting inv for msg(API disseminatePreEncryptedMsg command):', hexlify(inventoryHash) - if BMConfigParser.safeGetBoolean("network", "asyncore"): + if BMConfigParser().safeGetBoolean("network", "asyncore"): queues.invQueue.put((toStreamNumber, inventoryHash)) else: protocol.broadcastToSendDataQueues(( @@ -908,7 +908,7 @@ class MySimpleXMLRPCRequestHandler(SimpleXMLRPCRequestHandler): objectType, pubkeyStreamNumber, payload, int(time.time()) + TTL,'') with shared.printLock: print 'broadcasting inv within API command disseminatePubkey with hash:', hexlify(inventoryHash) - if BMConfigParser.safeGetBoolean("network", "asyncore"): + if BMConfigParser().safeGetBoolean("network", "asyncore"): queues.invQueue.put((pubkeyStreamNumber, inventoryHash)) else: protocol.broadcastToSendDataQueues(( diff --git a/src/class_singleWorker.py b/src/class_singleWorker.py index ff357b70..acdf70ae 100644 --- a/src/class_singleWorker.py +++ b/src/class_singleWorker.py @@ -192,7 +192,7 @@ class singleWorker(threading.Thread, StoppableThread): logger.info('broadcasting inv with hash: ' + hexlify(inventoryHash)) - if BMConfigParser.safeGetBoolean("network", "asyncore"): + if BMConfigParser().safeGetBoolean("network", "asyncore"): queues.invQueue.put((streamNumber, inventoryHash)) else: protocol.broadcastToSendDataQueues(( @@ -286,7 +286,7 @@ class singleWorker(threading.Thread, StoppableThread): logger.info('broadcasting inv with hash: ' + hexlify(inventoryHash)) - if BMConfigParser.safeGetBoolean("network", "asyncore"): + if BMConfigParser().safeGetBoolean("network", "asyncore"): queues.invQueue.put((streamNumber, inventoryHash)) else: protocol.broadcastToSendDataQueues(( @@ -380,7 +380,7 @@ class singleWorker(threading.Thread, StoppableThread): logger.info('broadcasting inv with hash: ' + hexlify(inventoryHash)) - if BMConfigParser.safeGetBoolean("network", "asyncore"): + if BMConfigParser().safeGetBoolean("network", "asyncore"): queues.invQueue.put((streamNumber, inventoryHash)) else: protocol.broadcastToSendDataQueues(( @@ -513,7 +513,7 @@ class singleWorker(threading.Thread, StoppableThread): objectType, streamNumber, payload, embeddedTime, tag) PendingUpload().add(inventoryHash) logger.info('sending inv (within sendBroadcast function) for object: ' + hexlify(inventoryHash)) - if BMConfigParser.safeGetBoolean("network", "asyncore"): + if BMConfigParser().safeGetBoolean("network", "asyncore"): queues.invQueue.put((streamNumber, inventoryHash)) else: protocol.broadcastToSendDataQueues(( @@ -846,7 +846,7 @@ class singleWorker(threading.Thread, StoppableThread): # not sending to a chan or one of my addresses queues.UISignalQueue.put(('updateSentItemStatusByAckdata', (ackdata, tr._translate("MainWindow", "Message sent. Waiting for acknowledgement. Sent on %1").arg(l10n.formatTimestamp())))) logger.info('Broadcasting inv for my msg(within sendmsg function):' + hexlify(inventoryHash)) - if BMConfigParser.safeGetBoolean("network", "asyncore"): + if BMConfigParser().safeGetBoolean("network", "asyncore"): queues.invQueue.put((toStreamNumber, inventoryHash)) else: protocol.broadcastToSendDataQueues(( @@ -952,7 +952,7 @@ class singleWorker(threading.Thread, StoppableThread): objectType, streamNumber, payload, embeddedTime, '') PendingUpload().add(inventoryHash) logger.info('sending inv (for the getpubkey message)') - if BMConfigParser.safeGetBoolean("network", "asyncore"): + if BMConfigParser().safeGetBoolean("network", "asyncore"): queues.invQueue.put((streamNumber, inventoryHash)) else: protocol.broadcastToSendDataQueues(( From 02a07e5119b69d01744ee667935d760eb31af70f Mon Sep 17 00:00:00 2001 From: Peter Surda Date: Mon, 29 May 2017 03:16:14 +0200 Subject: [PATCH 068/407] Asyncore update - default to true (original attempt didn't work correctly) --- src/api.py | 4 ++-- src/bitmessagemain.py | 6 +++--- src/class_singleWorker.py | 12 ++++++------ src/network/stats.py | 14 +++++++------- 4 files changed, 18 insertions(+), 18 deletions(-) diff --git a/src/api.py b/src/api.py index ee371b38..212cd278 100644 --- a/src/api.py +++ b/src/api.py @@ -858,7 +858,7 @@ class MySimpleXMLRPCRequestHandler(SimpleXMLRPCRequestHandler): objectType, toStreamNumber, encryptedPayload, int(time.time()) + TTL,'') with shared.printLock: print 'Broadcasting inv for msg(API disseminatePreEncryptedMsg command):', hexlify(inventoryHash) - if BMConfigParser().safeGetBoolean("network", "asyncore"): + if BMConfigParser().get("network", "asyncore"): queues.invQueue.put((toStreamNumber, inventoryHash)) else: protocol.broadcastToSendDataQueues(( @@ -908,7 +908,7 @@ class MySimpleXMLRPCRequestHandler(SimpleXMLRPCRequestHandler): objectType, pubkeyStreamNumber, payload, int(time.time()) + TTL,'') with shared.printLock: print 'broadcasting inv within API command disseminatePubkey with hash:', hexlify(inventoryHash) - if BMConfigParser().safeGetBoolean("network", "asyncore"): + if BMConfigParser().get("network", "asyncore"): queues.invQueue.put((pubkeyStreamNumber, inventoryHash)) else: protocol.broadcastToSendDataQueues(( diff --git a/src/bitmessagemain.py b/src/bitmessagemain.py index ca578c43..e6b8b56b 100755 --- a/src/bitmessagemain.py +++ b/src/bitmessagemain.py @@ -90,7 +90,7 @@ def connectToStream(streamNumber): if streamNumber*2+1 not in knownnodes.knownNodes: knownnodes.knownNodes[streamNumber*2+1] = {} - if BMConfigParser().safeGetBoolean("network", "asyncore"): + if BMConfigParser().get("network", "asyncore"): BMConnectionPool().connectToStream(streamNumber) else: for i in range(state.maximumNumberOfHalfOpenConnections): @@ -266,7 +266,7 @@ class Main: singleAPIThread.daemon = True # close the main program even if there are threads left singleAPIThread.start() - if BMConfigParser().safeGetBoolean("network", "asyncore"): + if BMConfigParser().get("network", "asyncore"): asyncoreThread = BMNetworkThread() asyncoreThread.daemon = True asyncoreThread.start() @@ -282,7 +282,7 @@ class Main: connectToStream(1) - if not BMConfigParser().safeGetBoolean("network", "asyncore"): + if not BMConfigParser().get("network", "asyncore"): singleListenerThread = singleListener() singleListenerThread.setup(selfInitiatedConnections) singleListenerThread.daemon = True # close the main program even if there are threads left diff --git a/src/class_singleWorker.py b/src/class_singleWorker.py index acdf70ae..07c768a4 100644 --- a/src/class_singleWorker.py +++ b/src/class_singleWorker.py @@ -192,7 +192,7 @@ class singleWorker(threading.Thread, StoppableThread): logger.info('broadcasting inv with hash: ' + hexlify(inventoryHash)) - if BMConfigParser().safeGetBoolean("network", "asyncore"): + if BMConfigParser().get("network", "asyncore"): queues.invQueue.put((streamNumber, inventoryHash)) else: protocol.broadcastToSendDataQueues(( @@ -286,7 +286,7 @@ class singleWorker(threading.Thread, StoppableThread): logger.info('broadcasting inv with hash: ' + hexlify(inventoryHash)) - if BMConfigParser().safeGetBoolean("network", "asyncore"): + if BMConfigParser().get("network", "asyncore"): queues.invQueue.put((streamNumber, inventoryHash)) else: protocol.broadcastToSendDataQueues(( @@ -380,7 +380,7 @@ class singleWorker(threading.Thread, StoppableThread): logger.info('broadcasting inv with hash: ' + hexlify(inventoryHash)) - if BMConfigParser().safeGetBoolean("network", "asyncore"): + if BMConfigParser().get("network", "asyncore"): queues.invQueue.put((streamNumber, inventoryHash)) else: protocol.broadcastToSendDataQueues(( @@ -513,7 +513,7 @@ class singleWorker(threading.Thread, StoppableThread): objectType, streamNumber, payload, embeddedTime, tag) PendingUpload().add(inventoryHash) logger.info('sending inv (within sendBroadcast function) for object: ' + hexlify(inventoryHash)) - if BMConfigParser().safeGetBoolean("network", "asyncore"): + if BMConfigParser().get("network", "asyncore"): queues.invQueue.put((streamNumber, inventoryHash)) else: protocol.broadcastToSendDataQueues(( @@ -846,7 +846,7 @@ class singleWorker(threading.Thread, StoppableThread): # not sending to a chan or one of my addresses queues.UISignalQueue.put(('updateSentItemStatusByAckdata', (ackdata, tr._translate("MainWindow", "Message sent. Waiting for acknowledgement. Sent on %1").arg(l10n.formatTimestamp())))) logger.info('Broadcasting inv for my msg(within sendmsg function):' + hexlify(inventoryHash)) - if BMConfigParser().safeGetBoolean("network", "asyncore"): + if BMConfigParser().get("network", "asyncore"): queues.invQueue.put((toStreamNumber, inventoryHash)) else: protocol.broadcastToSendDataQueues(( @@ -952,7 +952,7 @@ class singleWorker(threading.Thread, StoppableThread): objectType, streamNumber, payload, embeddedTime, '') PendingUpload().add(inventoryHash) logger.info('sending inv (for the getpubkey message)') - if BMConfigParser().safeGetBoolean("network", "asyncore"): + if BMConfigParser().get("network", "asyncore"): queues.invQueue.put((streamNumber, inventoryHash)) else: protocol.broadcastToSendDataQueues(( diff --git a/src/network/stats.py b/src/network/stats.py index 36baf2cb..b348098c 100644 --- a/src/network/stats.py +++ b/src/network/stats.py @@ -15,7 +15,7 @@ lastSentBytes = 0 currentSentSpeed = 0 def connectedHostsList(): - if BMConfigParser().safeGetBoolean("network", "asyncore"): + if BMConfigParser().get("network", "asyncore"): retval = [] for i in BMConnectionPool().inboundConnections.values() + BMConnectionPool().outboundConnections.values(): if not i.connected: @@ -29,14 +29,14 @@ def connectedHostsList(): return shared.connectedHostsList.items() def sentBytes(): - if BMConfigParser().safeGetBoolean("network", "asyncore"): + if BMConfigParser().get("network", "asyncore"): return asyncore.sentBytes else: return throttle.SendThrottle().total def uploadSpeed(): global lastSentTimestamp, lastSentBytes, currentSentSpeed - if BMConfigParser().safeGetBoolean("network", "asyncore"): + if BMConfigParser().get("network", "asyncore"): currentTimestamp = time.time() if int(lastSentTimestamp) < int(currentTimestamp): currentSentBytes = asyncore.sentBytes @@ -48,14 +48,14 @@ def uploadSpeed(): return throttle.sendThrottle().getSpeed() def receivedBytes(): - if BMConfigParser().safeGetBoolean("network", "asyncore"): + if BMConfigParser().get("network", "asyncore"): return asyncore.receivedBytes else: return throttle.ReceiveThrottle().total def downloadSpeed(): global lastReceivedTimestamp, lastReceivedBytes, currentReceivedSpeed - if BMConfigParser().safeGetBoolean("network", "asyncore"): + if BMConfigParser().get("network", "asyncore"): currentTimestamp = time.time() if int(lastReceivedTimestamp) < int(currentTimestamp): currentReceivedBytes = asyncore.receivedBytes @@ -67,7 +67,7 @@ def downloadSpeed(): return throttle.ReceiveThrottle().getSpeed() def pendingDownload(): - if BMConfigParser().safeGetBoolean("network", "asyncore"): + if BMConfigParser().get("network", "asyncore"): tmp = {} for connection in BMConnectionPool().inboundConnections.values() + BMConnectionPool().outboundConnections.values(): for k in connection.objectsNewToMe.keys(): @@ -77,7 +77,7 @@ def pendingDownload(): return PendingDownloadQueue.totalSize() def pendingUpload(): - if BMConfigParser().safeGetBoolean("network", "asyncore"): + if BMConfigParser().get("network", "asyncore"): return 0 tmp = {} for connection in BMConnectionPool().inboundConnections.values() + BMConnectionPool().outboundConnections.values(): From e7525d47bedafe920a7f4f11c6f69e9af9ecc500 Mon Sep 17 00:00:00 2001 From: Peter Surda Date: Mon, 29 May 2017 11:26:56 +0200 Subject: [PATCH 069/407] Disable memory usage logging - it looks like it's Unix specific and doesn't work on windows --- src/class_singleCleaner.py | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/class_singleCleaner.py b/src/class_singleCleaner.py index 22b83079..19c3ca52 100644 --- a/src/class_singleCleaner.py +++ b/src/class_singleCleaner.py @@ -1,5 +1,4 @@ import threading -import resource import shared import time import sys @@ -122,8 +121,6 @@ class singleCleaner(threading.Thread, StoppableThread): # TODO: cleanup pending upload / download - logger.info("Memory usage %s (kB)", resource.getrusage(resource.RUSAGE_SELF).ru_maxrss) - if state.shutdown == 0: self.stop.wait(singleCleaner.cycleLength) From 74f1a74a8c6ae0a0df18c2ea4b97c751bfb260e9 Mon Sep 17 00:00:00 2001 From: Peter Surda Date: Mon, 29 May 2017 11:30:56 +0200 Subject: [PATCH 070/407] Make SO_REUSEPORT optional - apparently not available on Windows --- src/network/udp.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/network/udp.py b/src/network/udp.py index 5412083f..48a805d8 100644 --- a/src/network/udp.py +++ b/src/network/udp.py @@ -60,7 +60,10 @@ class UDPSocket(BMProto): self.socket = sock self.socket.setsockopt(socket.SOL_SOCKET, socket.SO_BROADCAST, 1) self.socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) - self.socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEPORT, 1) + try: + self.socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEPORT, 1) + except AttributeError: + pass self.destination = state.Peer(self.socket.getsockname()[0], self.socket.getsockname()[1]) ObjectTracker.__init__(self) self.connecting = False From a5c1b0c52955e95da90c38f5f0522f912065bf3c Mon Sep 17 00:00:00 2001 From: Peter Surda Date: Mon, 29 May 2017 12:56:59 +0200 Subject: [PATCH 071/407] Asyncore fixes - better handling of WSA* checks on non-windows systems - handle EBADF on Windows/select - better timeouts / loop lengths in main asyncore loop and spawning new connections - remove InvThread prints --- src/bitmessagemain.py | 6 ------ src/network/asyncore_pollchoose.py | 30 ++++++++++++++++++------------ src/network/connectionpool.py | 10 +++++++++- src/network/invthread.py | 3 +++ 4 files changed, 30 insertions(+), 19 deletions(-) diff --git a/src/bitmessagemain.py b/src/bitmessagemain.py index e6b8b56b..57eab27a 100755 --- a/src/bitmessagemain.py +++ b/src/bitmessagemain.py @@ -102,12 +102,6 @@ def _fixSocket(): if sys.platform.startswith('linux'): socket.SO_BINDTODEVICE = 25 - if not sys.platform.startswith('win'): - errno.WSAEWOULDBLOCK = errno.EWOULDBLOCK - errno.WSAENETUNREACH = errno.ENETUNREACH - errno.WSAECONNREFUSED = errno.ECONNREFUSED - errno.WSAEHOSTUNREACH = errno.EHOSTUNREACH - if not sys.platform.startswith('win'): return diff --git a/src/network/asyncore_pollchoose.py b/src/network/asyncore_pollchoose.py index 25a5b3fb..02a362a1 100644 --- a/src/network/asyncore_pollchoose.py +++ b/src/network/asyncore_pollchoose.py @@ -61,8 +61,9 @@ from errno import EALREADY, EINPROGRESS, EWOULDBLOCK, ECONNRESET, EINVAL, \ errorcode try: from errno import WSAEWOULDBLOCK -except: - pass +except (ImportError, AttributeError): + WSAEWOULDBLOCK = EWOULDBLOCK + from ssl import SSLError, SSL_ERROR_WANT_READ, SSL_ERROR_WANT_WRITE _DISCONNECTED = frozenset((ECONNRESET, ENOTCONN, ESHUTDOWN, ECONNABORTED, EPIPE, @@ -199,6 +200,9 @@ def select_poller(timeout=0.0, map=None): r, w, e = select.select(r, w, e, timeout) except KeyboardInterrupt: return + except socket.error as err: + if err.args[0] in (EBADF): + return for fd in random.sample(r, len(r)): obj = map.get(fd) @@ -369,12 +373,18 @@ def loop(timeout=30.0, use_poll=False, map=None, count=None, # then poll poller(timeout, map) else: - timeout /= count + if timeout == 0: + deadline = 0 + else: + deadline = time.time() + timeout while map and count > 0: # fill buckets first update_sent() update_received() - poller(timeout, map) + subtimeout = deadline - time.time() + if subtimeout <= 0: + break + poller(subtimeout, map) # then poll count = count - 1 @@ -555,10 +565,8 @@ class dispatcher: else: raise except socket.error as why: - if why.args[0] in (EAGAIN, EWOULDBLOCK) or \ - (sys.platform.startswith('win') and \ - err.errno == WSAEWOULDBLOCK): - return 0 + if why.args[0] in (EAGAIN, EWOULDBLOCK, WSAEWOULDBLOCK): + return 0 elif why.args[0] in _DISCONNECTED: self.handle_close() return 0 @@ -582,10 +590,8 @@ class dispatcher: raise except socket.error as why: # winsock sometimes raises ENOTCONN - if why.args[0] in (EAGAIN, EWOULDBLOCK) or \ - (sys.platform.startswith('win') and \ - err.errno == WSAEWOULDBLOCK): - return b'' + if why.args[0] in (EAGAIN, EWOULDBLOCK, WSAEWOULDBLOCK): + return b'' if why.args[0] in _DISCONNECTED: self.handle_close() return b'' diff --git a/src/network/connectionpool.py b/src/network/connectionpool.py index d1a6b6ee..e9bc56c8 100644 --- a/src/network/connectionpool.py +++ b/src/network/connectionpool.py @@ -19,6 +19,7 @@ import state @Singleton class BMConnectionPool(object): + def __init__(self): asyncore.set_rates( BMConfigParser().safeGetInt("bitmessagesettings", "maxdownloadrate") * 1024, @@ -28,6 +29,8 @@ class BMConnectionPool(object): self.listeningSockets = {} self.udpSockets = {} self.streams = [] + self.lastSpawned = 0 + self.spawnWait = 0.3 self.bootstrapped = False @@ -146,6 +149,8 @@ class BMConnectionPool(object): if e.errno == errno.ENETUNREACH: continue + self.lastSpawned = time.time() + if acceptConnections and len(self.listeningSockets) == 0: self.startListening() logger.info('Listening for incoming connections.') @@ -169,7 +174,10 @@ class BMConnectionPool(object): # while len(asyncore.socket_map) > 0 and state.shutdown == 0: # print "loop, state = %s" % (proxy.state) - asyncore.loop(timeout=2.0, count=1) + loopTime = float(self.spawnWait) + if self.lastSpawned < time.time() - self.spawnWait: + loopTime = 1.0 + asyncore.loop(timeout=loopTime, count=10) for i in self.inboundConnections.values() + self.outboundConnections.values(): minTx = time.time() - 20 diff --git a/src/network/invthread.py b/src/network/invthread.py index 37fb7094..a880607a 100644 --- a/src/network/invthread.py +++ b/src/network/invthread.py @@ -1,3 +1,4 @@ +from binascii import hexlify import collections import Queue import random @@ -35,6 +36,7 @@ class InvThread(threading.Thread, StoppableThread): try: (stream, hash) = invQueue.get(False) self.holdHash (stream, hash) + #print "Holding hash %i, %s" % (stream, hexlify(hash)) except Queue.Empty: break @@ -50,6 +52,7 @@ class InvThread(threading.Thread, StoppableThread): except KeyError: continue if len(hashes) > 0: + #print "sending inv of %i" % (len(hashes)) connection.writeQueue.put(protocol.CreatePacket('inv', addresses.encodeVarint(len(hashes)) + b"".join(hashes))) self.collectionOfInvs[iterator] = {} iterator += 1 From 97c44b97f41a289399fecb1857658049bbf9a9e7 Mon Sep 17 00:00:00 2001 From: Peter Surda Date: Mon, 29 May 2017 13:14:25 +0200 Subject: [PATCH 072/407] Asyncore update - handle WSAENOTSOCK --- src/network/asyncore_pollchoose.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/network/asyncore_pollchoose.py b/src/network/asyncore_pollchoose.py index 02a362a1..39576849 100644 --- a/src/network/asyncore_pollchoose.py +++ b/src/network/asyncore_pollchoose.py @@ -63,6 +63,10 @@ try: from errno import WSAEWOULDBLOCK except (ImportError, AttributeError): WSAEWOULDBLOCK = EWOULDBLOCK +try: + from errno import WSAENOTSOCK +except (ImportError, AttributeError): + WSAENOTSOCK = ENOTSOCK from ssl import SSLError, SSL_ERROR_WANT_READ, SSL_ERROR_WANT_WRITE @@ -201,7 +205,7 @@ def select_poller(timeout=0.0, map=None): except KeyboardInterrupt: return except socket.error as err: - if err.args[0] in (EBADF): + if err.args[0] in (EBADF, WSAENOTSOCK): return for fd in random.sample(r, len(r)): From bdaa939e2cd93d2dd4d781a04accd9687db04837 Mon Sep 17 00:00:00 2001 From: Peter Surda Date: Mon, 29 May 2017 13:39:26 +0200 Subject: [PATCH 073/407] ENOTSOCK fix --- src/network/asyncore_pollchoose.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/network/asyncore_pollchoose.py b/src/network/asyncore_pollchoose.py index 39576849..ae18e95e 100644 --- a/src/network/asyncore_pollchoose.py +++ b/src/network/asyncore_pollchoose.py @@ -57,7 +57,7 @@ import warnings import os from errno import EALREADY, EINPROGRESS, EWOULDBLOCK, ECONNRESET, EINVAL, \ ENOTCONN, ESHUTDOWN, EISCONN, EBADF, ECONNABORTED, EPIPE, EAGAIN, \ - ECONNREFUSED, EHOSTUNREACH, \ + ECONNREFUSED, EHOSTUNREACH, ENOTSOCK, \ errorcode try: from errno import WSAEWOULDBLOCK From 0aa5dbd95846a8a7a746f2726c082782435ad3db Mon Sep 17 00:00:00 2001 From: Peter Surda Date: Mon, 29 May 2017 14:35:08 +0200 Subject: [PATCH 074/407] Asyncore update - shutdown fix --- src/network/announcethread.py | 2 +- src/network/invthread.py | 2 +- src/network/networkthread.py | 5 +++-- src/network/receivequeuethread.py | 3 ++- 4 files changed, 7 insertions(+), 5 deletions(-) diff --git a/src/network/announcethread.py b/src/network/announcethread.py index 29ed301e..85e69877 100644 --- a/src/network/announcethread.py +++ b/src/network/announcethread.py @@ -21,7 +21,7 @@ class AnnounceThread(threading.Thread, StoppableThread): def run(self): lastSelfAnnounced = 0 - while not self._stopped: + while not self._stopped and state.shutdown == 0: processed = 0 if lastSelfAnnounced < time.time() - UDPSocket.announceInterval: self.announceSelf() diff --git a/src/network/invthread.py b/src/network/invthread.py index a880607a..1fd4a401 100644 --- a/src/network/invthread.py +++ b/src/network/invthread.py @@ -53,7 +53,7 @@ class InvThread(threading.Thread, StoppableThread): continue if len(hashes) > 0: #print "sending inv of %i" % (len(hashes)) - connection.writeQueue.put(protocol.CreatePacket('inv', addresses.encodeVarint(len(hashes)) + b"".join(hashes))) + connection.writeQueue.put(protocol.CreatePacket('inv', addresses.encodeVarint(len(hashes)) + "".join(hashes))) self.collectionOfInvs[iterator] = {} iterator += 1 iterator %= InvThread.size diff --git a/src/network/networkthread.py b/src/network/networkthread.py index 54c58f12..7e98bcc0 100644 --- a/src/network/networkthread.py +++ b/src/network/networkthread.py @@ -5,6 +5,7 @@ from debug import logger from helper_threading import StoppableThread import network.asyncore_pollchoose as asyncore from network.connectionpool import BMConnectionPool +import state class BMNetworkThread(threading.Thread, StoppableThread): def __init__(self): @@ -15,10 +16,11 @@ class BMNetworkThread(threading.Thread, StoppableThread): logger.info("init asyncore thread") def run(self): - while not self._stopped: + while not self._stopped and state.shutdown == 0: BMConnectionPool().loop() def stopThread(self): + super(BMNetworkThread, self).stopThread() for i in BMConnectionPool().listeningSockets: try: i.close() @@ -37,4 +39,3 @@ class BMNetworkThread(threading.Thread, StoppableThread): # just in case asyncore.close_all() - super(BMNetworkThread, self).stopThread() diff --git a/src/network/receivequeuethread.py b/src/network/receivequeuethread.py index a0a2c4b8..b31b82b4 100644 --- a/src/network/receivequeuethread.py +++ b/src/network/receivequeuethread.py @@ -10,6 +10,7 @@ from inventory import Inventory from network.connectionpool import BMConnectionPool from network.bmproto import BMProto import protocol +import state class ReceiveQueueThread(threading.Thread, StoppableThread): def __init__(self): @@ -21,7 +22,7 @@ class ReceiveQueueThread(threading.Thread, StoppableThread): def run(self): lastprinted = int(time.time()) - while not self._stopped: + while not self._stopped and state.shutdown == 0: if lastprinted < int(time.time()): lastprinted = int(time.time()) processed = 0 From 1ccfd41c3f9e9633852279b1367d9684816bfee9 Mon Sep 17 00:00:00 2001 From: Peter Surda Date: Mon, 29 May 2017 14:41:02 +0200 Subject: [PATCH 075/407] Asyncore updates - fix connected to myself check --- src/network/bmproto.py | 1 + 1 file changed, 1 insertion(+) diff --git a/src/network/bmproto.py b/src/network/bmproto.py index eb372aa7..2ad1853d 100644 --- a/src/network/bmproto.py +++ b/src/network/bmproto.py @@ -428,6 +428,7 @@ class BMProto(AdvancedDispatcher, ObjectTracker): errorText="I'm connected to myself. Closing connection.")) logger.debug ("Closed connection to %s because I'm connected to myself.", str(self.destination)) + return False return True From 7f381c0c2580a30c8d5ab7e1f140a6d0e8f0c7ff Mon Sep 17 00:00:00 2001 From: Peter Surda Date: Mon, 29 May 2017 14:52:31 +0200 Subject: [PATCH 076/407] Asyncore update - incoming object handling fix --- src/network/bmproto.py | 3 +-- src/network/connectionpool.py | 2 +- src/network/invthread.py | 6 +++++- 3 files changed, 7 insertions(+), 4 deletions(-) diff --git a/src/network/bmproto.py b/src/network/bmproto.py index 2ad1853d..87d24bba 100644 --- a/src/network/bmproto.py +++ b/src/network/bmproto.py @@ -298,8 +298,7 @@ class BMProto(AdvancedDispatcher, ObjectTracker): self.object.objectType, self.object.streamNumber, self.payload[objectOffset:], self.object.expiresTime, self.object.tag) objectProcessorQueue.put((self.object.objectType,self.object.data)) #DownloadQueue().task_done(self.object.inventoryHash) - network.connectionpool.BMConnectionPool().handleReceivedObject(self, self.object.streamNumber, self.object.inventoryHash) - invQueue.put((self.object.streamNumber, self.object.inventoryHash)) + invQueue.put((self.object.streamNumber, self.object.inventoryHash, self)) #ObjUploadQueue().put(UploadElem(self.object.streamNumber, self.object.inventoryHash)) #broadcastToSendDataQueues((streamNumber, 'advertiseobject', inventoryHash)) return True diff --git a/src/network/connectionpool.py b/src/network/connectionpool.py index e9bc56c8..1e2ed311 100644 --- a/src/network/connectionpool.py +++ b/src/network/connectionpool.py @@ -34,7 +34,7 @@ class BMConnectionPool(object): self.bootstrapped = False - def handleReceivedObject(self, connection, streamNumber, hashid): + def handleReceivedObject(self, streamNumber, hashid, connection = None): for i in self.inboundConnections.values() + self.outboundConnections.values(): if not isinstance(i, network.bmproto.BMProto): continue diff --git a/src/network/invthread.py b/src/network/invthread.py index 1fd4a401..2f0ab594 100644 --- a/src/network/invthread.py +++ b/src/network/invthread.py @@ -34,7 +34,11 @@ class InvThread(threading.Thread, StoppableThread): while not state.shutdown: while True: try: - (stream, hash) = invQueue.get(False) + data = invQueue.get(False) + if len(data) == 2: + BMConnectionPool().handleReceivedObject(self, data[0], data[1]) + else: + BMConnectionPool().handleReceivedObject(self, data[0], data[1], data[3]) self.holdHash (stream, hash) #print "Holding hash %i, %s" % (stream, hexlify(hash)) except Queue.Empty: From 3a543efd83e56b120494a23a548118d37a01c486 Mon Sep 17 00:00:00 2001 From: Peter Surda Date: Mon, 29 May 2017 14:54:13 +0200 Subject: [PATCH 077/407] Typo --- src/network/invthread.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/network/invthread.py b/src/network/invthread.py index 2f0ab594..1081adeb 100644 --- a/src/network/invthread.py +++ b/src/network/invthread.py @@ -38,7 +38,7 @@ class InvThread(threading.Thread, StoppableThread): if len(data) == 2: BMConnectionPool().handleReceivedObject(self, data[0], data[1]) else: - BMConnectionPool().handleReceivedObject(self, data[0], data[1], data[3]) + BMConnectionPool().handleReceivedObject(self, data[0], data[1], data[2]) self.holdHash (stream, hash) #print "Holding hash %i, %s" % (stream, hexlify(hash)) except Queue.Empty: From 11d02b1e415f7876eb0fb0c83c79f15a7fb547ca Mon Sep 17 00:00:00 2001 From: Peter Surda Date: Mon, 29 May 2017 14:59:42 +0200 Subject: [PATCH 078/407] typo --- src/network/invthread.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/network/invthread.py b/src/network/invthread.py index 1081adeb..61401568 100644 --- a/src/network/invthread.py +++ b/src/network/invthread.py @@ -36,9 +36,9 @@ class InvThread(threading.Thread, StoppableThread): try: data = invQueue.get(False) if len(data) == 2: - BMConnectionPool().handleReceivedObject(self, data[0], data[1]) + BMConnectionPool().handleReceivedObject(data[0], data[1]) else: - BMConnectionPool().handleReceivedObject(self, data[0], data[1], data[2]) + BMConnectionPool().handleReceivedObject(data[0], data[1], data[2]) self.holdHash (stream, hash) #print "Holding hash %i, %s" % (stream, hexlify(hash)) except Queue.Empty: From abaa2c72e558b106f1db19617274b485da610cc0 Mon Sep 17 00:00:00 2001 From: Peter Surda Date: Mon, 29 May 2017 15:04:22 +0200 Subject: [PATCH 079/407] typo --- src/network/invthread.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/network/invthread.py b/src/network/invthread.py index 61401568..6d1828e1 100644 --- a/src/network/invthread.py +++ b/src/network/invthread.py @@ -39,7 +39,7 @@ class InvThread(threading.Thread, StoppableThread): BMConnectionPool().handleReceivedObject(data[0], data[1]) else: BMConnectionPool().handleReceivedObject(data[0], data[1], data[2]) - self.holdHash (stream, hash) + self.holdHash (data[0], data[1]) #print "Holding hash %i, %s" % (stream, hexlify(hash)) except Queue.Empty: break From fa9811f4264a2b231744e946b3936e50edab1992 Mon Sep 17 00:00:00 2001 From: Peter Surda Date: Tue, 30 May 2017 23:53:43 +0200 Subject: [PATCH 080/407] Asyncore update - duplicate checking implemented - connection pool vs. socket closing cleanup --- src/network/bmobject.py | 5 +++++ src/network/bmproto.py | 38 ++++++++++++++++------------------- src/network/connectionpool.py | 17 +++++++++++----- src/network/tcp.py | 1 + 4 files changed, 35 insertions(+), 26 deletions(-) diff --git a/src/network/bmobject.py b/src/network/bmobject.py index 2c3fb59c..e16a6937 100644 --- a/src/network/bmobject.py +++ b/src/network/bmobject.py @@ -3,6 +3,7 @@ import time from addresses import calculateInventoryHash from debug import logger +from inventory import Inventory import protocol import state @@ -64,6 +65,10 @@ class BMObject(object): logger.debug('The streamNumber %s isn\'t one we are interested in.' % self.streamNumber) raise BMObjectUnwantedStreamError() + def checkAlreadyHave(self): + if self.inventoryHash in Inventory(): + raise BMObjectAlreadyHaveError() + def checkMessage(self): return diff --git a/src/network/bmproto.py b/src/network/bmproto.py index 87d24bba..ffd79056 100644 --- a/src/network/bmproto.py +++ b/src/network/bmproto.py @@ -280,27 +280,24 @@ class BMProto(AdvancedDispatcher, ObjectTracker): self.object.checkProofOfWorkSufficient() self.object.checkEOLSanity() self.object.checkStream() + self.object.checkAlreadyHave() - try: - if self.object.objectType == protocol.OBJECT_GETPUBKEY: - self.object.checkGetpubkey() - elif self.object.objectType == protocol.OBJECT_PUBKEY: - self.object.checkPubkey(self.payload[self.payloadOffset:self.payloadOffset+32]) - elif self.object.objectType == protocol.OBJECT_MSG: - self.object.checkMessage() - elif self.object.objectType == protocol.OBJECT_BROADCAST: - self.object.checkBroadcast(self.payload[self.payloadOffset:self.payloadOffset+32]) - # other objects don't require other types of tests - except BMObjectAlreadyHaveError: - pass - else: - Inventory()[self.object.inventoryHash] = ( - self.object.objectType, self.object.streamNumber, self.payload[objectOffset:], self.object.expiresTime, self.object.tag) - objectProcessorQueue.put((self.object.objectType,self.object.data)) - #DownloadQueue().task_done(self.object.inventoryHash) - invQueue.put((self.object.streamNumber, self.object.inventoryHash, self)) - #ObjUploadQueue().put(UploadElem(self.object.streamNumber, self.object.inventoryHash)) - #broadcastToSendDataQueues((streamNumber, 'advertiseobject', inventoryHash)) + if self.object.objectType == protocol.OBJECT_GETPUBKEY: + self.object.checkGetpubkey() + elif self.object.objectType == protocol.OBJECT_PUBKEY: + self.object.checkPubkey(self.payload[self.payloadOffset:self.payloadOffset+32]) + elif self.object.objectType == protocol.OBJECT_MSG: + self.object.checkMessage() + elif self.object.objectType == protocol.OBJECT_BROADCAST: + self.object.checkBroadcast(self.payload[self.payloadOffset:self.payloadOffset+32]) + # other objects don't require other types of tests + Inventory()[self.object.inventoryHash] = ( + self.object.objectType, self.object.streamNumber, self.payload[objectOffset:], self.object.expiresTime, self.object.tag) + objectProcessorQueue.put((self.object.objectType,self.object.data)) + #DownloadQueue().task_done(self.object.inventoryHash) + invQueue.put((self.object.streamNumber, self.object.inventoryHash, self)) + #ObjUploadQueue().put(UploadElem(self.object.streamNumber, self.object.inventoryHash)) + #broadcastToSendDataQueues((streamNumber, 'advertiseobject', inventoryHash)) return True def _decode_addr(self): @@ -456,5 +453,4 @@ class BMProto(AdvancedDispatcher, ObjectTracker): #traceback.print_stack() else: logger.debug("%s:%i: closing, %s", self.destination.host, self.destination.port, reason) - network.connectionpool.BMConnectionPool().removeConnection(self) AdvancedDispatcher.close(self) diff --git a/src/network/connectionpool.py b/src/network/connectionpool.py index 1e2ed311..ef2fb26b 100644 --- a/src/network/connectionpool.py +++ b/src/network/connectionpool.py @@ -64,7 +64,9 @@ class BMConnectionPool(object): def removeConnection(self, connection): if isinstance(connection, network.udp.UDPSocket): - return + del self.udpSockets[connection.destination.host] + if isinstance(connection, network.tcp.TCPServer): + del self.listeningSockets[state.Peer(connection.destination.host, connection.destination.port)] elif connection.isOutbound: try: del self.outboundConnections[connection.destination] @@ -99,9 +101,10 @@ class BMConnectionPool(object): def startUDPSocket(self, bind=None): if bind is None: host = self.getListeningIP() - self.udpSockets[host] = network.udp.UDPSocket(host=host) + udpSocket = network.udp.UDPSocket(host=host) else: - self.udpSockets[bind] = network.udp.UDPSocket(host=bind) + udpSocket = network.udp.UDPSocket(host=bind) + self.udpSockets[udpSocket.destination.host] = udpSocket def loop(self): # defaults to empty loop if outbound connections are maxed @@ -164,12 +167,10 @@ class BMConnectionPool(object): if len(self.listeningSockets) > 0 and not acceptConnections: for i in self.listeningSockets: i.close() - self.listeningSockets = {} logger.info('Stopped listening for incoming connections.') if len(self.udpSockets) > 0 and not acceptConnections: for i in self.udpSockets: i.close() - self.udpSockets = {} logger.info('Stopped udp sockets.') # while len(asyncore.socket_map) > 0 and state.shutdown == 0: @@ -179,6 +180,7 @@ class BMConnectionPool(object): loopTime = 1.0 asyncore.loop(timeout=loopTime, count=10) + reaper = [] for i in self.inboundConnections.values() + self.outboundConnections.values(): minTx = time.time() - 20 if i.fullyEstablished: @@ -188,3 +190,8 @@ class BMConnectionPool(object): i.writeQueue.put(protocol.CreatePacket('ping')) else: i.close("Timeout (%is)" % (time.time() - i.lastTx)) + for i in self.inboundConnections.values() + self.outboundConnections.values() + self.listeningSockets.values() + self.udpSockets.values(): + if not (i.accepting or i.connecting or i.connected): + reaper.append(i) + for i in reaper: + self.removeConnection(i) diff --git a/src/network/tcp.py b/src/network/tcp.py index 42d5a831..2a2188ea 100644 --- a/src/network/tcp.py +++ b/src/network/tcp.py @@ -198,6 +198,7 @@ class TCPServer(AdvancedDispatcher): self.create_socket(socket.AF_INET, socket.SOCK_STREAM) self.set_reuse_addr() self.bind((host, port)) + self.destination = state.Peer(host, port) self.listen(5) def handle_accept(self): From f23c169eec7f71b4b217cfb842fb1224b0af398a Mon Sep 17 00:00:00 2001 From: Peter Surda Date: Wed, 31 May 2017 00:04:21 +0200 Subject: [PATCH 081/407] Don't connect to myself - track local IP+port of incoming connections and don't connect to them in the future --- src/network/connectionpool.py | 3 +++ src/network/tcp.py | 1 + src/state.py | 2 ++ 3 files changed, 6 insertions(+) diff --git a/src/network/connectionpool.py b/src/network/connectionpool.py index ef2fb26b..4afada16 100644 --- a/src/network/connectionpool.py +++ b/src/network/connectionpool.py @@ -134,6 +134,9 @@ class BMConnectionPool(object): continue if chosen.host in self.inboundConnections: continue + # don't connect to self + if chosen.host in state.ownAddresses: + continue #for c in self.outboundConnections: # if chosen == c.destination: diff --git a/src/network/tcp.py b/src/network/tcp.py index 2a2188ea..3ce4cc52 100644 --- a/src/network/tcp.py +++ b/src/network/tcp.py @@ -205,6 +205,7 @@ class TCPServer(AdvancedDispatcher): pair = self.accept() if pair is not None: sock, addr = pair + state.ownAddresses[state.Peer(sock.getsockname()[0], sock.getsockname()[1])] = True try: network.connectionpool.BMConnectionPool().addConnection(TCPConnection(sock=sock)) except socket.error: diff --git a/src/state.py b/src/state.py index ff8a143d..618b6c92 100644 --- a/src/state.py +++ b/src/state.py @@ -25,6 +25,8 @@ maximumNumberOfHalfOpenConnections = 0 invThread = None +ownAddresses = {} + # If the trustedpeer option is specified in keys.dat then this will # contain a Peer which will be connected to instead of using the # addresses advertised by other peers. The client will only connect to From d9e3349eeb861316d955c11acac41b201c9717f9 Mon Sep 17 00:00:00 2001 From: Peter Surda Date: Wed, 31 May 2017 00:22:07 +0200 Subject: [PATCH 082/407] Fix own IP detection - minor bug in the previous commit --- src/network/connectionpool.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/network/connectionpool.py b/src/network/connectionpool.py index 4afada16..447e8170 100644 --- a/src/network/connectionpool.py +++ b/src/network/connectionpool.py @@ -135,7 +135,7 @@ class BMConnectionPool(object): if chosen.host in self.inboundConnections: continue # don't connect to self - if chosen.host in state.ownAddresses: + if chosen in state.ownAddresses: continue #for c in self.outboundConnections: From e522f015a8351029733d09f5593e01bb164fcbb6 Mon Sep 17 00:00:00 2001 From: Peter Surda Date: Wed, 31 May 2017 10:15:47 +0200 Subject: [PATCH 083/407] Network status updates - only update processed numbers once every 2 seconds - moved inventory lookups to the main inventory so now all storage modules work with it --- src/bitmessageqt/networkstatus.py | 9 ++++++--- src/inventory.py | 3 +++ src/storage/sqlite.py | 1 - 3 files changed, 9 insertions(+), 4 deletions(-) diff --git a/src/bitmessageqt/networkstatus.py b/src/bitmessageqt/networkstatus.py index ef9e4c42..ce30b4c7 100644 --- a/src/bitmessageqt/networkstatus.py +++ b/src/bitmessageqt/networkstatus.py @@ -49,17 +49,17 @@ class NetworkStatus(QtGui.QWidget, RetranslateMixin): self.labelSyncStatus.setText(_translate("networkstatus", "Object(s) to be synced: %n", None, QtCore.QCoreApplication.CodecForTr, network.stats.pendingDownload() + network.stats.pendingUpload())) def updateNumberOfMessagesProcessed(self): - self.updateNumberOfObjectsToBeSynced() +# self.updateNumberOfObjectsToBeSynced() self.labelMessageCount.setText(_translate( "networkstatus", "Processed %n person-to-person message(s).", None, QtCore.QCoreApplication.CodecForTr, shared.numberOfMessagesProcessed)) def updateNumberOfBroadcastsProcessed(self): - self.updateNumberOfObjectsToBeSynced() +# self.updateNumberOfObjectsToBeSynced() self.labelBroadcastCount.setText(_translate( "networkstatus", "Processed %n broadcast message(s).", None, QtCore.QCoreApplication.CodecForTr, shared.numberOfBroadcastsProcessed)) def updateNumberOfPubkeysProcessed(self): - self.updateNumberOfObjectsToBeSynced() +# self.updateNumberOfObjectsToBeSynced() self.labelPubkeyCount.setText(_translate( "networkstatus", "Processed %n public key(s).", None, QtCore.QCoreApplication.CodecForTr, shared.numberOfPubkeysProcessed)) @@ -128,6 +128,9 @@ class NetworkStatus(QtGui.QWidget, RetranslateMixin): Inventory().numberOfInventoryLookupsPerformed = 0 self.updateNumberOfBytes() self.updateNumberOfObjectsToBeSynced() + self.updateNumberOfMessagesProcessed() + self.updateNumberOfBroadcastsProcessed() + self.updateNumberOfPubkeysProcessed() def retranslateUi(self): super(QtGui.QWidget, self).retranslateUi() diff --git a/src/inventory.py b/src/inventory.py index b676415b..598021fb 100644 --- a/src/inventory.py +++ b/src/inventory.py @@ -23,10 +23,13 @@ class Inventory(): self._className = "storage." + self._moduleName + "." + self._moduleName.title() + "Inventory" self._inventoryClass = eval(self._className) self._realInventory = self._inventoryClass() + self.numberOfInventoryLookupsPerformed = 0 # cheap inheritance copied from asyncore def __getattr__(self, attr): try: + if attr == "__contains__": + self.numberOfInventoryLookupsPerformed += 1 realRet = getattr(self._realInventory, attr) except AttributeError: raise AttributeError("%s instance has no attribute '%s'" %(self.__class__.__name__, attr)) diff --git a/src/storage/sqlite.py b/src/storage/sqlite.py index 38e9c0a2..96733a36 100644 --- a/src/storage/sqlite.py +++ b/src/storage/sqlite.py @@ -15,7 +15,6 @@ class SqliteInventory(InventoryStorage): def __contains__(self, hash): with self.lock: - self.numberOfInventoryLookupsPerformed += 1 if hash in self._inventory: return True return bool(sqlQuery('SELECT 1 FROM inventory WHERE hash=?', hash)) From 2555f692ebd9e969f270a9c39b9e73b2ae635402 Mon Sep 17 00:00:00 2001 From: Peter Surda Date: Wed, 31 May 2017 10:16:30 +0200 Subject: [PATCH 084/407] Network status update part 2 - only update processed items every 2 seconds --- src/class_objectProcessor.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/class_objectProcessor.py b/src/class_objectProcessor.py index 253e6808..d146dfd2 100644 --- a/src/class_objectProcessor.py +++ b/src/class_objectProcessor.py @@ -181,8 +181,8 @@ class objectProcessor(threading.Thread): def processpubkey(self, data): pubkeyProcessingStartTime = time.time() shared.numberOfPubkeysProcessed += 1 - queues.UISignalQueue.put(( - 'updateNumberOfPubkeysProcessed', 'no data')) +# queues.UISignalQueue.put(( +# 'updateNumberOfPubkeysProcessed', 'no data')) embeddedTime, = unpack('>Q', data[8:16]) readPosition = 20 # bypass the nonce, time, and object type addressVersion, varintLength = decodeVarint( @@ -330,8 +330,8 @@ class objectProcessor(threading.Thread): def processmsg(self, data): messageProcessingStartTime = time.time() shared.numberOfMessagesProcessed += 1 - queues.UISignalQueue.put(( - 'updateNumberOfMessagesProcessed', 'no data')) +# queues.UISignalQueue.put(( +# 'updateNumberOfMessagesProcessed', 'no data')) readPosition = 20 # bypass the nonce, time, and object type msgVersion, msgVersionLength = decodeVarint(data[readPosition:readPosition + 9]) if msgVersion != 1: @@ -600,8 +600,8 @@ class objectProcessor(threading.Thread): def processbroadcast(self, data): messageProcessingStartTime = time.time() shared.numberOfBroadcastsProcessed += 1 - queues.UISignalQueue.put(( - 'updateNumberOfBroadcastsProcessed', 'no data')) +# queues.UISignalQueue.put(( +# 'updateNumberOfBroadcastsProcessed', 'no data')) inventoryHash = calculateInventoryHash(data) readPosition = 20 # bypass the nonce, time, and object type broadcastVersion, broadcastVersionLength = decodeVarint( From 18988ae2e6c04e43d80f0cdade95fabebb12ad49 Mon Sep 17 00:00:00 2001 From: Peter Surda Date: Wed, 31 May 2017 10:17:36 +0200 Subject: [PATCH 085/407] Asyncore updates - performance optimisation, reduce number of loops when waiting for protocol headers / commands --- src/network/bmproto.py | 6 +++--- src/network/tcp.py | 2 +- src/network/udp.py | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/network/bmproto.py b/src/network/bmproto.py index ffd79056..f59be314 100644 --- a/src/network/bmproto.py +++ b/src/network/bmproto.py @@ -78,13 +78,13 @@ class BMProto(AdvancedDispatcher, ObjectTracker): if self.magic != 0xE9BEB4D9: # skip 1 byte in order to sync self.bm_proto_reset() - self.set_state("bm_header", 1) + self.set_state("bm_header", length=1, expectBytes=protocol.Header.size) logger.debug("Bad magic") self.close() return False if self.payloadLength > BMProto.maxMessageSize: self.invalid = True - self.set_state("bm_command", protocol.Header.size, expectBytes=self.payloadLength) + self.set_state("bm_command", length=protocol.Header.size, expectBytes=self.payloadLength) return True def state_bm_command(self): @@ -130,7 +130,7 @@ class BMProto(AdvancedDispatcher, ObjectTracker): self.close() return False if retval: - self.set_state("bm_header", self.payloadLength) + self.set_state("bm_header", length=self.payloadLength, expectBytes=protocol.Header.size) self.bm_proto_reset() # else assume the command requires a different state to follow return True diff --git a/src/network/tcp.py b/src/network/tcp.py index 3ce4cc52..f6d0c7ac 100644 --- a/src/network/tcp.py +++ b/src/network/tcp.py @@ -69,7 +69,7 @@ class TCPConnection(BMProto, TLSDispatcher): ObjectTracker.__init__(self) UISignalQueue.put(('updateNetworkStatusTab', 'no data')) self.bm_proto_reset() - self.set_state("bm_header") + self.set_state("bm_header", expectBytes=protocol.Header.size) def antiIntersectionDelay(self, initial = False): # estimated time for a small object to propagate across the whole network diff --git a/src/network/udp.py b/src/network/udp.py index 48a805d8..8c710997 100644 --- a/src/network/udp.py +++ b/src/network/udp.py @@ -68,7 +68,7 @@ class UDPSocket(BMProto): ObjectTracker.__init__(self) self.connecting = False self.connected = True - self.set_state("bm_header") + self.set_state("bm_header", expectBytes=protocol.Header.size) def state_bm_command(self): BMProto.state_bm_command(self) From 4c17a1800648da0bcca5d1aca5302d361bc18e17 Mon Sep 17 00:00:00 2001 From: Peter Surda Date: Wed, 31 May 2017 23:34:06 +0200 Subject: [PATCH 086/407] Don't send invs to unestablished connections --- src/network/connectionpool.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/network/connectionpool.py b/src/network/connectionpool.py index 447e8170..517b3c98 100644 --- a/src/network/connectionpool.py +++ b/src/network/connectionpool.py @@ -38,6 +38,8 @@ class BMConnectionPool(object): for i in self.inboundConnections.values() + self.outboundConnections.values(): if not isinstance(i, network.bmproto.BMProto): continue + if not i.fullyEstablished: + continue try: del i.objectsNewToMe[hashid] except KeyError: From d75d920a687ff3aa03cd1ccf51c1b23ec17b5b1c Mon Sep 17 00:00:00 2001 From: Peter Surda Date: Fri, 2 Jun 2017 07:09:35 +0200 Subject: [PATCH 087/407] Asyncore updates - clean object tracking dictionaries in the cleaner thread - clean up close / handle_close - add locking to tracking dictionaries --- src/class_singleCleaner.py | 4 ++++ src/network/advanceddispatcher.py | 2 +- src/network/bmproto.py | 8 ++++---- src/network/connectionpool.py | 16 +++++++++------- src/network/objectracker.py | 25 ++++++++++++++++++------- src/network/tcp.py | 10 ++++------ 6 files changed, 40 insertions(+), 25 deletions(-) diff --git a/src/class_singleCleaner.py b/src/class_singleCleaner.py index 19c3ca52..e6d98989 100644 --- a/src/class_singleCleaner.py +++ b/src/class_singleCleaner.py @@ -119,6 +119,10 @@ class singleCleaner(threading.Thread, StoppableThread): if thread.isAlive() and hasattr(thread, 'downloadQueue'): thread.downloadQueue.clear() + # inv/object tracking + for connection in BMConnectionPool().inboundConnections.values() + BMConnectionPool().outboundConnections.values(): + connection.clean() + # TODO: cleanup pending upload / download if state.shutdown == 0: diff --git a/src/network/advanceddispatcher.py b/src/network/advanceddispatcher.py index 5f8a94aa..89a7423d 100644 --- a/src/network/advanceddispatcher.py +++ b/src/network/advanceddispatcher.py @@ -106,7 +106,7 @@ class AdvancedDispatcher(asyncore.dispatcher): def state_close(self): pass - def close(self): + def handle_close(self): self.read_buf = b"" self.write_buf = b"" self.state = "close" diff --git a/src/network/bmproto.py b/src/network/bmproto.py index f59be314..8c727f00 100644 --- a/src/network/bmproto.py +++ b/src/network/bmproto.py @@ -80,7 +80,7 @@ class BMProto(AdvancedDispatcher, ObjectTracker): self.bm_proto_reset() self.set_state("bm_header", length=1, expectBytes=protocol.Header.size) logger.debug("Bad magic") - self.close() + self.handle_close("Bad magic") return False if self.payloadLength > BMProto.maxMessageSize: self.invalid = True @@ -127,7 +127,7 @@ class BMProto(AdvancedDispatcher, ObjectTracker): else: #print "Skipping command %s due to invalid data" % (self.command) logger.debug("Closing due to invalid command %s", self.command) - self.close() + self.handle_close("Invalid command %s" % (self.command)) return False if retval: self.set_state("bm_header", length=self.payloadLength, expectBytes=protocol.Header.size) @@ -445,7 +445,7 @@ class BMProto(AdvancedDispatcher, ObjectTracker): payload += struct.pack('>H', peer.port) # remote port return protocol.CreatePacket('addr', payload) - def close(self, reason=None): + def handle_close(self, reason=None): self.set_state("close") if reason is None: #logger.debug("%s:%i: closing, %s", self.destination.host, self.destination.port, ''.join(traceback.format_stack())) @@ -453,4 +453,4 @@ class BMProto(AdvancedDispatcher, ObjectTracker): #traceback.print_stack() else: logger.debug("%s:%i: closing, %s", self.destination.host, self.destination.port, reason) - AdvancedDispatcher.close(self) + AdvancedDispatcher.handle_close(self) diff --git a/src/network/connectionpool.py b/src/network/connectionpool.py index 517b3c98..6aa6e49b 100644 --- a/src/network/connectionpool.py +++ b/src/network/connectionpool.py @@ -31,7 +31,6 @@ class BMConnectionPool(object): self.streams = [] self.lastSpawned = 0 self.spawnWait = 0.3 - self.bootstrapped = False def handleReceivedObject(self, streamNumber, hashid, connection = None): @@ -41,12 +40,15 @@ class BMConnectionPool(object): if not i.fullyEstablished: continue try: - del i.objectsNewToMe[hashid] + with i.objectsNewToMeLock: + del i.objectsNewToMe[hashid] except KeyError: - i.objectsNewToThem[hashid] = True + with i.objectsNewToThemLock: + i.objectsNewToThem[hashid] = True if i == connection: try: - del i.objectsNewToThem[hashid] + with i.objectsNewToThemLock: + del i.objectsNewToThem[hashid] except KeyError: pass @@ -171,11 +173,11 @@ class BMConnectionPool(object): logger.info('Starting UDP socket(s).') if len(self.listeningSockets) > 0 and not acceptConnections: for i in self.listeningSockets: - i.close() + i.handle_close() logger.info('Stopped listening for incoming connections.') if len(self.udpSockets) > 0 and not acceptConnections: for i in self.udpSockets: - i.close() + i.handle_close() logger.info('Stopped udp sockets.') # while len(asyncore.socket_map) > 0 and state.shutdown == 0: @@ -194,7 +196,7 @@ class BMConnectionPool(object): if i.fullyEstablished: i.writeQueue.put(protocol.CreatePacket('ping')) else: - i.close("Timeout (%is)" % (time.time() - i.lastTx)) + i.handle_close("Timeout (%is)" % (time.time() - i.lastTx)) for i in self.inboundConnections.values() + self.outboundConnections.values() + self.listeningSockets.values() + self.udpSockets.values(): if not (i.accepting or i.connecting or i.connected): reaper.append(i) diff --git a/src/network/objectracker.py b/src/network/objectracker.py index 246916b9..d073d78a 100644 --- a/src/network/objectracker.py +++ b/src/network/objectracker.py @@ -1,5 +1,6 @@ from Queue import Queue import time +from threading import RLock from inventory import Inventory from network.downloadqueue import DownloadQueue @@ -29,11 +30,14 @@ class ObjectTracker(object): def __init__(self): self.objectsNewToMe = {} + self.objectsNewToMeLock = RLock() self.objectsNewToThem = {} + self.objectsNewToThemLock = RLock() self.downloadPending = 0 self.downloadQueue = Queue() self.initInvBloom() self.initAddrBloom() + self.lastCleaned = time.time() def initInvBloom(self): if haveBloom: @@ -48,15 +52,20 @@ class ObjectTracker(object): error_rate=ObjectTracker.invErrorRate) def clean(self): - if self.lastcleaned < time.time() - BMQueues.invCleanPeriod: + if self.lastCleaned < time.time() - ObjectTracker.invCleanPeriod: if haveBloom: if PendingDownloadQueue().size() == 0: self.initInvBloom() self.initAddrBloom() - else: - # release memory - self.objectsNewToMe = self.objectsNewToMe.copy() - self.objectsNewToThem = self.objectsNewToThem.copy() + else: + # release memory + with self.objectsNewToMeLock: + tmp = self.objectsNewToMe.copy() + self.objectsNewToMe = tmp + with self.objectsNewToThemLock: + tmp = self.objectsNewToThem.copy() + self.objectsNewToThem = tmp + self.lastCleaned = time.time() def hasObj(self, hashid): if haveBloom: @@ -69,11 +78,13 @@ class ObjectTracker(object): self.invBloom.add(hashId) elif hashId in Inventory(): try: - del self.objectsNewToThem[hashId] + with self.objectsNewToThemLock: + del self.objectsNewToThem[hashId] except KeyError: pass else: - self.objectsNewToMe[hashId] = True + with self.objectsNewToMeLock: + self.objectsNewToMe[hashId] = True # self.DownloadQueue.put(hashId) def hasAddr(self, addr): diff --git a/src/network/tcp.py b/src/network/tcp.py index f6d0c7ac..d3b2f862 100644 --- a/src/network/tcp.py +++ b/src/network/tcp.py @@ -149,10 +149,10 @@ class TCPConnection(BMProto, TLSDispatcher): def handle_connect_event(self): try: - asyncore.dispatcher.handle_connect_event(self) + AdvancedDispatcher.handle_connect_event(self) except socket.error as e: if e.errno in asyncore._DISCONNECTED: - self.close("Connection failed") + logger.debug("%s:%i: Connection failed: %s" % (self.destination.host, self.destination.port, str(e))) return self.writeQueue.put(protocol.assembleVersionMessage(self.destination.host, self.destination.port, network.connectionpool.BMConnectionPool().streams, False)) #print "%s:%i: Sending version" % (self.destination.host, self.destination.port) @@ -162,15 +162,13 @@ class TCPConnection(BMProto, TLSDispatcher): try: TLSDispatcher.handle_read(self) except socket.error as e: - #print "%s:%i: socket error: %s" % (self.destination.host, self.destination.port, str(e)) - self.close() + logger.debug("%s:%i: Handle read fail: %s" % (self.destination.host, self.destination.port, str(e))) def handle_write(self): try: TLSDispatcher.handle_write(self) except socket.error as e: - #print "%s:%i: socket error: %s" % (self.destination.host, self.destination.port, str(e)) - self.close() + logger.debug("%s:%i: Handle write fail: %s" % (self.destination.host, self.destination.port, str(e))) class Socks5BMConnection(Socks5Connection, TCPConnection): From 6044df5adf84b767e559b482ab90aead7eb1c4ab Mon Sep 17 00:00:00 2001 From: Peter Surda Date: Fri, 2 Jun 2017 15:43:35 +0200 Subject: [PATCH 088/407] Optional storing of expired and off-stream objects - a new config file option, network/acceptmismatch, allows the inventory to store objects that expired or are from a stream we're not interested in. Having this on will prevent re-requesting objects that other nodes incorrectly advertise. It defaults to false --- src/bmconfigparser.py | 1 + src/network/bmproto.py | 16 ++++++++++------ 2 files changed, 11 insertions(+), 6 deletions(-) diff --git a/src/bmconfigparser.py b/src/bmconfigparser.py index b727ad65..bfa7e396 100644 --- a/src/bmconfigparser.py +++ b/src/bmconfigparser.py @@ -21,6 +21,7 @@ BMConfigDefaults = { }, "inventory": { "storage": "sqlite", + "acceptmismatch": False, }, "zlib": { 'maxsize': 1048576 diff --git a/src/network/bmproto.py b/src/network/bmproto.py index 8c727f00..722438f5 100644 --- a/src/network/bmproto.py +++ b/src/network/bmproto.py @@ -5,8 +5,6 @@ import math import time import socket import struct -import random -import traceback from addresses import calculateInventoryHash from debug import logger @@ -278,8 +276,16 @@ class BMProto(AdvancedDispatcher, ObjectTracker): raise BMProtoExcessiveDataError() self.object.checkProofOfWorkSufficient() - self.object.checkEOLSanity() - self.object.checkStream() + try: + self.object.checkEOLSanity() + except BMObjectExpiredError: + if not BMConfigParser().get("inventory", "acceptmismatch"): + raise + try: + self.object.checkStream() + except BMObjectUnwantedStreamError: + if not BMConfigParser().get("inventory", "acceptmismatch"): + raise self.object.checkAlreadyHave() if self.object.objectType == protocol.OBJECT_GETPUBKEY: @@ -448,9 +454,7 @@ class BMProto(AdvancedDispatcher, ObjectTracker): def handle_close(self, reason=None): self.set_state("close") if reason is None: - #logger.debug("%s:%i: closing, %s", self.destination.host, self.destination.port, ''.join(traceback.format_stack())) logger.debug("%s:%i: closing", self.destination.host, self.destination.port) - #traceback.print_stack() else: logger.debug("%s:%i: closing, %s", self.destination.host, self.destination.port, reason) AdvancedDispatcher.handle_close(self) From f78f1a718b952a79b6ce780c378e0dbf9151fad3 Mon Sep 17 00:00:00 2001 From: Scott Date: Fri, 2 Jun 2017 18:53:13 -0600 Subject: [PATCH 089/407] Change api.py --- src/api.py | 43 ++++++++++++++++++++++++------------------- 1 file changed, 24 insertions(+), 19 deletions(-) diff --git a/src/api.py b/src/api.py index 212cd278..8c959832 100644 --- a/src/api.py +++ b/src/api.py @@ -13,8 +13,9 @@ if __name__ == "__main__": sys.exit(0) from SimpleXMLRPCServer import SimpleXMLRPCRequestHandler, SimpleXMLRPCServer +import base64 import json -from binascii import hexlify +from binascii import hexlify, unhexlify import shared import time @@ -139,7 +140,10 @@ class MySimpleXMLRPCRequestHandler(SimpleXMLRPCRequestHandler): def _decode(self, text, decode_type): try: - return text.decode(decode_type) + if decode_type == 'hex': + return unhexlify(text) + elif decode_type == 'base64': + return base64.b64decode(text) except Exception as e: raise APIError(22, "Decode error - " + str(e) + ". Had trouble while decoding string: " + repr(text)) @@ -180,7 +184,7 @@ class MySimpleXMLRPCRequestHandler(SimpleXMLRPCRequestHandler): chan = False label = BMConfigParser().get(addressInKeysFile, 'label') if method == 'listAddresses2': - label = label.encode('base64') + label = base64.b64encode(label) data += json.dumps({'label': label, 'address': addressInKeysFile, 'stream': streamNumber, 'enabled': BMConfigParser().getboolean(addressInKeysFile, 'enabled'), 'chan': chan}, indent=4, separators=(',', ': ')) data += ']}' @@ -201,7 +205,7 @@ class MySimpleXMLRPCRequestHandler(SimpleXMLRPCRequestHandler): label = shared.fixPotentiallyInvalidUTF8Data(label) if len(data) > 20: data += ',' - data += json.dumps({'label':label.encode('base64'), 'address': address}, indent=4, separators=(',', ': ')) + data += json.dumps({'label':base64.b64encode(label), 'address': address}, indent=4, separators=(',', ': ')) data += ']}' return data @@ -483,8 +487,10 @@ class MySimpleXMLRPCRequestHandler(SimpleXMLRPCRequestHandler): message = shared.fixPotentiallyInvalidUTF8Data(message) if len(data) > 25: data += ',' - data += json.dumps({'msgid': hexlify(msgid), 'toAddress': toAddress, 'fromAddress': fromAddress, 'subject': subject.encode( - 'base64'), 'message': message.encode('base64'), 'encodingType': encodingtype, 'receivedTime': received, 'read': read}, indent=4, separators=(',', ': ')) + data += json.dumps({'msgid': hexlify(msgid), 'toAddress': toAddress, + 'fromAddress': fromAddress, 'subject': base64.b64encode(subject), + 'message': base64.b64encode(message), 'encodingType': encodingtype, + 'receivedTime': received, 'read': read}, indent=4, separators=(',', ': ')) data += ']}' return data @@ -521,7 +527,7 @@ class MySimpleXMLRPCRequestHandler(SimpleXMLRPCRequestHandler): msgid, toAddress, fromAddress, subject, received, message, encodingtype, read = row subject = shared.fixPotentiallyInvalidUTF8Data(subject) message = shared.fixPotentiallyInvalidUTF8Data(message) - data += json.dumps({'msgid':hexlify(msgid), 'toAddress':toAddress, 'fromAddress':fromAddress, 'subject':subject.encode('base64'), 'message':message.encode('base64'), 'encodingType':encodingtype, 'receivedTime':received, 'read': read}, indent=4, separators=(',', ': ')) + data += json.dumps({'msgid':hexlify(msgid), 'toAddress':toAddress, 'fromAddress':fromAddress, 'subject':base64.b64encode(subject), 'message':base64.b64encode(message), 'encodingType':encodingtype, 'receivedTime':received, 'read': read}, indent=4, separators=(',', ': ')) data += ']}' return data @@ -534,7 +540,7 @@ class MySimpleXMLRPCRequestHandler(SimpleXMLRPCRequestHandler): message = shared.fixPotentiallyInvalidUTF8Data(message) if len(data) > 25: data += ',' - data += json.dumps({'msgid':hexlify(msgid), 'toAddress':toAddress, 'fromAddress':fromAddress, 'subject':subject.encode('base64'), 'message':message.encode('base64'), 'encodingType':encodingtype, 'lastActionTime':lastactiontime, 'status':status, 'ackData':hexlify(ackdata)}, indent=4, separators=(',', ': ')) + data += json.dumps({'msgid':hexlify(msgid), 'toAddress':toAddress, 'fromAddress':fromAddress, 'subject':base64.b64encode(subject), 'message':base64.b64encode(message), 'encodingType':encodingtype, 'lastActionTime':lastactiontime, 'status':status, 'ackData':hexlify(ackdata)}, indent=4, separators=(',', ': ')) data += ']}' return data @@ -561,7 +567,7 @@ class MySimpleXMLRPCRequestHandler(SimpleXMLRPCRequestHandler): message = shared.fixPotentiallyInvalidUTF8Data(message) if len(data) > 25: data += ',' - data += json.dumps({'msgid':hexlify(msgid), 'toAddress':toAddress, 'fromAddress':fromAddress, 'subject':subject.encode('base64'), 'message':message.encode('base64'), 'encodingType':encodingtype, 'receivedTime':received}, indent=4, separators=(',', ': ')) + data += json.dumps({'msgid':hexlify(msgid), 'toAddress':toAddress, 'fromAddress':fromAddress, 'subject':base64.b64encode(subject), 'message':base64.b64encode(message), 'encodingType':encodingtype, 'receivedTime':received}, indent=4, separators=(',', ': ')) data += ']}' return data @@ -575,7 +581,7 @@ class MySimpleXMLRPCRequestHandler(SimpleXMLRPCRequestHandler): msgid, toAddress, fromAddress, subject, lastactiontime, message, encodingtype, status, ackdata = row subject = shared.fixPotentiallyInvalidUTF8Data(subject) message = shared.fixPotentiallyInvalidUTF8Data(message) - data += json.dumps({'msgid':hexlify(msgid), 'toAddress':toAddress, 'fromAddress':fromAddress, 'subject':subject.encode('base64'), 'message':message.encode('base64'), 'encodingType':encodingtype, 'lastActionTime':lastactiontime, 'status':status, 'ackData':hexlify(ackdata)}, indent=4, separators=(',', ': ')) + data += json.dumps({'msgid':hexlify(msgid), 'toAddress':toAddress, 'fromAddress':fromAddress, 'subject':base64.b64encode(subject), 'message':base64.b64encode(message), 'encodingType':encodingtype, 'lastActionTime':lastactiontime, 'status':status, 'ackData':hexlify(ackdata)}, indent=4, separators=(',', ': ')) data += ']}' return data @@ -592,7 +598,7 @@ class MySimpleXMLRPCRequestHandler(SimpleXMLRPCRequestHandler): message = shared.fixPotentiallyInvalidUTF8Data(message) if len(data) > 25: data += ',' - data += json.dumps({'msgid':hexlify(msgid), 'toAddress':toAddress, 'fromAddress':fromAddress, 'subject':subject.encode('base64'), 'message':message.encode('base64'), 'encodingType':encodingtype, 'lastActionTime':lastactiontime, 'status':status, 'ackData':hexlify(ackdata)}, indent=4, separators=(',', ': ')) + data += json.dumps({'msgid':hexlify(msgid), 'toAddress':toAddress, 'fromAddress':fromAddress, 'subject':base64.b64encode(subject), 'message':base64.b64encode(message), 'encodingType':encodingtype, 'lastActionTime':lastactiontime, 'status':status, 'ackData':hexlify(ackdata)}, indent=4, separators=(',', ': ')) data += ']}' return data @@ -607,7 +613,7 @@ class MySimpleXMLRPCRequestHandler(SimpleXMLRPCRequestHandler): msgid, toAddress, fromAddress, subject, lastactiontime, message, encodingtype, status, ackdata = row subject = shared.fixPotentiallyInvalidUTF8Data(subject) message = shared.fixPotentiallyInvalidUTF8Data(message) - data += json.dumps({'msgid':hexlify(msgid), 'toAddress':toAddress, 'fromAddress':fromAddress, 'subject':subject.encode('base64'), 'message':message.encode('base64'), 'encodingType':encodingtype, 'lastActionTime':lastactiontime, 'status':status, 'ackData':hexlify(ackdata)}, indent=4, separators=(',', ': ')) + data += json.dumps({'msgid':hexlify(msgid), 'toAddress':toAddress, 'fromAddress':fromAddress, 'subject':base64.b64encode(subject), 'message':base64.b64encode(message), 'encodingType':encodingtype, 'lastActionTime':lastactiontime, 'status':status, 'ackData':hexlify(ackdata)}, indent=4, separators=(',', ': ')) data += ']}' return data @@ -816,15 +822,12 @@ class MySimpleXMLRPCRequestHandler(SimpleXMLRPCRequestHandler): def ListSubscriptions(self, params): queryreturn = sqlQuery('''SELECT label, address, enabled FROM subscriptions''') - data = '{"subscriptions":[' + data = {'subscriptions': []} for row in queryreturn: label, address, enabled = row label = shared.fixPotentiallyInvalidUTF8Data(label) - if len(data) > 20: - data += ',' - data += json.dumps({'label':label.encode('base64'), 'address': address, 'enabled': enabled == 1}, indent=4, separators=(',',': ')) - data += ']}' - return data + data['subscriptions'].append({'label':base64.b64encode(label), 'address': address, 'enabled': enabled == 1}) + return json.dumps(data, indent=4, separators=(',',': ')) def HandleDisseminatePreEncryptedMsg(self, params): # The device issuing this command to PyBitmessage supplies a msg object that has @@ -965,7 +968,7 @@ class MySimpleXMLRPCRequestHandler(SimpleXMLRPCRequestHandler): address, = params status, addressVersion, streamNumber, ripe = decodeAddress(address) return json.dumps({'status':status, 'addressVersion':addressVersion, - 'streamNumber':streamNumber, 'ripe':ripe.encode('base64')}, indent=4, + 'streamNumber':streamNumber, 'ripe':base64.b64encode(ripe)}, indent=4, separators=(',', ': ')) def HandleHelloWorld(self, params): @@ -1059,3 +1062,5 @@ class MySimpleXMLRPCRequestHandler(SimpleXMLRPCRequestHandler): except Exception as e: logger.exception(e) return "API Error 0021: Unexpected API Failure - %s" % str(e) + + From a9c0000c17bdd2b46930c3cfe20032f7aaee8896 Mon Sep 17 00:00:00 2001 From: Peter Surda Date: Sat, 3 Jun 2017 16:29:21 +0200 Subject: [PATCH 090/407] Treat some invalid objects as received - update to 6044df5adf84b767e559b482ab90aead7eb1c4ab - objects that are expired or in wrong stream are not re-requested anymore, even if they aren't stored in the inventory - the previous option "acceptmismatch" now only affects whether such objects are stored in the inventory --- src/network/bmproto.py | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/src/network/bmproto.py b/src/network/bmproto.py index 722438f5..e85da3f2 100644 --- a/src/network/bmproto.py +++ b/src/network/bmproto.py @@ -278,12 +278,17 @@ class BMProto(AdvancedDispatcher, ObjectTracker): self.object.checkProofOfWorkSufficient() try: self.object.checkEOLSanity() - except BMObjectExpiredError: - if not BMConfigParser().get("inventory", "acceptmismatch"): - raise - try: self.object.checkStream() - except BMObjectUnwantedStreamError: + except (BMObjectExpiredError, BMObjectUnwantedStreamError): + for connection in BMConnectionPool().inboundConnections.values() + BMConnectionPool().outboundConnections.values(): + try: + del connection.objectsNewtoThem[hashId] + except KeyError: + pass + try: + del connection.objectsNewToMe[hashId] + except KeyError: + pass if not BMConfigParser().get("inventory", "acceptmismatch"): raise self.object.checkAlreadyHave() From e8d9a7f183e6ce409f6e28b6948a8ea28c90e6bf Mon Sep 17 00:00:00 2001 From: Peter Surda Date: Sat, 3 Jun 2017 16:30:05 +0200 Subject: [PATCH 091/407] Asyncore connect handling - minor improvements in handling of connect events so that it's not processed twice --- src/network/advanceddispatcher.py | 8 ++++++++ src/network/asyncore_pollchoose.py | 4 ++-- src/network/tcp.py | 4 ++-- 3 files changed, 12 insertions(+), 4 deletions(-) diff --git a/src/network/advanceddispatcher.py b/src/network/advanceddispatcher.py index 89a7423d..dadb625b 100644 --- a/src/network/advanceddispatcher.py +++ b/src/network/advanceddispatcher.py @@ -1,3 +1,4 @@ +import socket import Queue import time @@ -99,6 +100,13 @@ class AdvancedDispatcher(asyncore.dispatcher): self.sentBytes += written self.slice_write_buf(written) + def handle_connect_event(self): + try: + asyncore.dispatcher.handle_connect_event(self) + except socket.error as e: + if e.args[0] not in asyncore._DISCONNECTED: + raise + def handle_connect(self): self.lastTx = time.time() self.process() diff --git a/src/network/asyncore_pollchoose.py b/src/network/asyncore_pollchoose.py index ae18e95e..8131b2b2 100644 --- a/src/network/asyncore_pollchoose.py +++ b/src/network/asyncore_pollchoose.py @@ -57,7 +57,7 @@ import warnings import os from errno import EALREADY, EINPROGRESS, EWOULDBLOCK, ECONNRESET, EINVAL, \ ENOTCONN, ESHUTDOWN, EISCONN, EBADF, ECONNABORTED, EPIPE, EAGAIN, \ - ECONNREFUSED, EHOSTUNREACH, ENOTSOCK, \ + ECONNREFUSED, EHOSTUNREACH, ENETUNREACH, ENOTSOCK, \ errorcode try: from errno import WSAEWOULDBLOCK @@ -71,7 +71,7 @@ except (ImportError, AttributeError): from ssl import SSLError, SSL_ERROR_WANT_READ, SSL_ERROR_WANT_WRITE _DISCONNECTED = frozenset((ECONNRESET, ENOTCONN, ESHUTDOWN, ECONNABORTED, EPIPE, - EBADF, ECONNREFUSED, EHOSTUNREACH)) + EBADF, ECONNREFUSED, EHOSTUNREACH, ENETUNREACH)) OP_READ = 1 OP_WRITE = 2 diff --git a/src/network/tcp.py b/src/network/tcp.py index d3b2f862..986a4b63 100644 --- a/src/network/tcp.py +++ b/src/network/tcp.py @@ -147,9 +147,9 @@ class TCPConnection(BMProto, TLSDispatcher): def sendBigInv(self): self.receiveQueue.put(("biginv", None)) - def handle_connect_event(self): + def handle_connect(self): try: - AdvancedDispatcher.handle_connect_event(self) + AdvancedDispatcher.handle_connect(self) except socket.error as e: if e.errno in asyncore._DISCONNECTED: logger.debug("%s:%i: Connection failed: %s" % (self.destination.host, self.destination.port, str(e))) From 7bde4e9445c5fcb04b75fe162ab84047d7e5f2ff Mon Sep 17 00:00:00 2001 From: Peter Surda Date: Sun, 4 Jun 2017 10:25:16 +0200 Subject: [PATCH 092/407] Missing module name in bmproto --- src/network/bmproto.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/network/bmproto.py b/src/network/bmproto.py index e85da3f2..97c354cd 100644 --- a/src/network/bmproto.py +++ b/src/network/bmproto.py @@ -280,7 +280,7 @@ class BMProto(AdvancedDispatcher, ObjectTracker): self.object.checkEOLSanity() self.object.checkStream() except (BMObjectExpiredError, BMObjectUnwantedStreamError): - for connection in BMConnectionPool().inboundConnections.values() + BMConnectionPool().outboundConnections.values(): + for connection in network.connectionpool.BMConnectionPool().inboundConnections.values() + network.connectionpool.BMConnectionPool().outboundConnections.values(): try: del connection.objectsNewtoThem[hashId] except KeyError: From 009a215224cdd3244fd0bdef0c06b28a424362da Mon Sep 17 00:00:00 2001 From: Peter Surda Date: Fri, 9 Jun 2017 10:07:51 +0200 Subject: [PATCH 093/407] Fix api connected hosts lists --- src/api.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/api.py b/src/api.py index 8c959832..eb2712af 100644 --- a/src/api.py +++ b/src/api.py @@ -31,6 +31,7 @@ import state from pyelliptic.openssl import OpenSSL import queues from struct import pack +import network.stats # Classes from helper_sql import sqlQuery,sqlExecute,SqlBulkExecute,sqlStoredProcedure @@ -959,7 +960,7 @@ class MySimpleXMLRPCRequestHandler(SimpleXMLRPCRequestHandler): networkStatus = 'connectedButHaveNotReceivedIncomingConnections' else: networkStatus = 'connectedAndReceivingIncomingConnections' - return json.dumps({'networkConnections':len(shared.connectedHostsList),'numberOfMessagesProcessed':shared.numberOfMessagesProcessed, 'numberOfBroadcastsProcessed':shared.numberOfBroadcastsProcessed, 'numberOfPubkeysProcessed':shared.numberOfPubkeysProcessed, 'networkStatus':networkStatus, 'softwareName':'PyBitmessage','softwareVersion':softwareVersion}, indent=4, separators=(',', ': ')) + return json.dumps({'networkConnections':len(network.stats.connectedHostsList()),'numberOfMessagesProcessed':shared.numberOfMessagesProcessed, 'numberOfBroadcastsProcessed':shared.numberOfBroadcastsProcessed, 'numberOfPubkeysProcessed':shared.numberOfPubkeysProcessed, 'networkStatus':networkStatus, 'softwareName':'PyBitmessage','softwareVersion':softwareVersion}, indent=4, separators=(',', ': ')) def HandleDecodeAddress(self, params): # Return a meaningful decoding of an address. From ae97f7abd86e9dacb185a9dcc1e8ea96a1f8865a Mon Sep 17 00:00:00 2001 From: Peter Surda Date: Sat, 10 Jun 2017 10:07:47 +0200 Subject: [PATCH 094/407] Typo --- src/bitmessagecurses/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/bitmessagecurses/__init__.py b/src/bitmessagecurses/__init__.py index 903b7e68..381d7c7a 100644 --- a/src/bitmessagecurses/__init__.py +++ b/src/bitmessagecurses/__init__.py @@ -927,7 +927,7 @@ def loadSent(): statstr = "Message sent at "+t+"." elif status == "doingmsgpow": statstr = "The proof of work required to send the message has been queued." - elif status == "askreceived": + elif status == "ackreceived": t = l10n.formatTimestamp(lastactiontime, False) statstr = "Acknowledgment of the message received at "+t+"." elif status == "broadcastqueued": From d34fdbb3f4b39975ebe6590d6474d6bb830121fb Mon Sep 17 00:00:00 2001 From: Peter Surda Date: Sat, 10 Jun 2017 10:08:40 +0200 Subject: [PATCH 095/407] Fix network status in api --- src/api.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/api.py b/src/api.py index eb2712af..52fb8e6e 100644 --- a/src/api.py +++ b/src/api.py @@ -954,9 +954,9 @@ class MySimpleXMLRPCRequestHandler(SimpleXMLRPCRequestHandler): return data def HandleClientStatus(self, params): - if len(shared.connectedHostsList) == 0: + if len(network.stats.connectedHostsList()) == 0: networkStatus = 'notConnected' - elif len(shared.connectedHostsList) > 0 and not shared.clientHasReceivedIncomingConnections: + elif len(network.stats.connectedHostsList()) > 0 and not shared.clientHasReceivedIncomingConnections: networkStatus = 'connectedButHaveNotReceivedIncomingConnections' else: networkStatus = 'connectedAndReceivingIncomingConnections' From f366447e9437497e6d1011df76acf81b33708e4f Mon Sep 17 00:00:00 2001 From: Peter Surda Date: Sat, 10 Jun 2017 10:09:14 +0200 Subject: [PATCH 096/407] Fix identicon imports --- src/bitmessageqt/utils.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/bitmessageqt/utils.py b/src/bitmessageqt/utils.py index 73367586..369d05ef 100644 --- a/src/bitmessageqt/utils.py +++ b/src/bitmessageqt/utils.py @@ -58,8 +58,8 @@ def identiconize(address): idcon_render = Pydenticon(addBMIfNotPresent(address)+identiconsuffix, size*3) rendering = idcon_render._render() data = rendering.convert("RGBA").tostring("raw", "RGBA") - qim = QImage(data, size, size, QImage.Format_ARGB32) - pix = QPixmap.fromImage(qim) + qim = QtGui.QImage(data, size, size, QtGui.QImage.Format_ARGB32) + pix = QtGui.QPixmap.fromImage(qim) idcon = QtGui.QIcon() idcon.addPixmap(pix, QtGui.QIcon.Normal, QtGui.QIcon.Off) return idcon From 7e8ee5132242e0dbb4cec77fe3846c116f3c0815 Mon Sep 17 00:00:00 2001 From: Peter Surda Date: Sat, 10 Jun 2017 10:09:44 +0200 Subject: [PATCH 097/407] Fallback umsgpack import fix --- src/helper_msgcoding.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/helper_msgcoding.py b/src/helper_msgcoding.py index a507685e..3e156754 100644 --- a/src/helper_msgcoding.py +++ b/src/helper_msgcoding.py @@ -3,7 +3,7 @@ try: import msgpack except ImportError: - import fallback.umsgpack as msgpack + import fallback.umsgpack.umsgpack as msgpack import string import zlib From 7f06cb7c2772a2e6d762163d12ae558946b4ee4a Mon Sep 17 00:00:00 2001 From: Peter Surda Date: Sat, 10 Jun 2017 10:10:59 +0200 Subject: [PATCH 098/407] Inventory flush fix in GUI - Fixes #1011 --- src/bitmessageqt/__init__.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/bitmessageqt/__init__.py b/src/bitmessageqt/__init__.py index 76f6e46d..9f4f0b04 100644 --- a/src/bitmessageqt/__init__.py +++ b/src/bitmessageqt/__init__.py @@ -77,7 +77,7 @@ from class_objectHashHolder import objectHashHolder from class_singleWorker import singleWorker from dialogs import AddAddressDialog from helper_generic import powQueueSize -from inventory import PendingDownloadQueue, PendingUpload, PendingUploadDeadlineException +from inventory import Inventory, PendingDownloadQueue, PendingUpload, PendingUploadDeadlineException import knownnodes import paths from proofofwork import getPowType @@ -2324,11 +2324,11 @@ class MyForm(settingsmixin.SMainWindow): # in the objectProcessorQueue to be processed if self.NewSubscriptionDialogInstance.ui.checkBoxDisplayMessagesAlreadyInInventory.isChecked(): status, addressVersion, streamNumber, ripe = decodeAddress(address) - shared.inventory.flush() + Inventory().flush() doubleHashOfAddressData = hashlib.sha512(hashlib.sha512(encodeVarint( addressVersion) + encodeVarint(streamNumber) + ripe).digest()).digest() tag = doubleHashOfAddressData[32:] - for value in shared.inventory.by_type_and_tag(3, tag): + for value in Inventory().by_type_and_tag(3, tag): queues.objectProcessorQueue.put((value.type, value.payload)) def click_pushButtonStatusIcon(self): @@ -4398,11 +4398,11 @@ class NewSubscriptionDialog(QtGui.QDialog): self.ui.checkBoxDisplayMessagesAlreadyInInventory.setText( _translate("MainWindow", "Address is an old type. We cannot display its past broadcasts.")) else: - shared.inventory.flush() + Inventory().flush() doubleHashOfAddressData = hashlib.sha512(hashlib.sha512(encodeVarint( addressVersion) + encodeVarint(streamNumber) + ripe).digest()).digest() tag = doubleHashOfAddressData[32:] - count = len(shared.inventory.by_type_and_tag(3, tag)) + count = len(Inventory().by_type_and_tag(3, tag)) if count == 0: self.ui.checkBoxDisplayMessagesAlreadyInInventory.setText( _translate("MainWindow", "There are no recent broadcasts from this address to display.")) From 8d41d5fcf603bd10e4db1c4cad7f3cc7e3b77b95 Mon Sep 17 00:00:00 2001 From: Peter Surda Date: Sat, 10 Jun 2017 10:11:21 +0200 Subject: [PATCH 099/407] default known nodes update --- src/defaultKnownNodes.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/defaultKnownNodes.py b/src/defaultKnownNodes.py index 4557c2e9..5559f1a5 100644 --- a/src/defaultKnownNodes.py +++ b/src/defaultKnownNodes.py @@ -16,7 +16,7 @@ def createDefaultKnownNodes(appdata): stream1[state.Peer('75.167.159.54', 8444)] = int(time.time()) stream1[state.Peer('95.165.168.168', 8444)] = int(time.time()) stream1[state.Peer('85.180.139.241', 8444)] = int(time.time()) - stream1[state.Peer('158.222.211.81', 8080)] = int(time.time()) + stream1[state.Peer('158.222.217.190', 8080)] = int(time.time()) stream1[state.Peer('178.62.12.187', 8448)] = int(time.time()) stream1[state.Peer('24.188.198.204', 8111)] = int(time.time()) stream1[state.Peer('109.147.204.113', 1195)] = int(time.time()) From 7deb7c3d4f76d8314a06d84f80e28abba02036ef Mon Sep 17 00:00:00 2001 From: Peter Surda Date: Sat, 10 Jun 2017 10:11:50 +0200 Subject: [PATCH 100/407] Typo --- src/highlevelcrypto.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/highlevelcrypto.py b/src/highlevelcrypto.py index 5e7f549c..8729ec5c 100644 --- a/src/highlevelcrypto.py +++ b/src/highlevelcrypto.py @@ -44,7 +44,7 @@ def sign(msg,hexPrivkey): # SHA256. Eventually this will become the default return makeCryptor(hexPrivkey).sign(msg, digest_alg=OpenSSL.EVP_sha256) else: - raise ValueError("Unknown digest algorithm %s" % (digestAlgo)) + raise ValueError("Unknown digest algorithm %s" % (digestAlg)) # Verifies with hex public key def verify(msg,sig,hexPubkey): # As mentioned above, we must upgrade gracefully to use SHA256. So From cba749088a297327175af68613ca1944275d2b8b Mon Sep 17 00:00:00 2001 From: Peter Surda Date: Sat, 10 Jun 2017 10:13:49 +0200 Subject: [PATCH 101/407] Asyncore updates - mainly work on proxy support, but it's still not fully working - minor bugfixes --- src/network/asyncore_pollchoose.py | 2 +- src/network/bmproto.py | 9 ++++++--- src/network/connectionpool.py | 3 +++ src/network/objectracker.py | 1 + src/network/proxy.py | 20 +++++++++++++++++++- src/network/socks4a.py | 2 +- src/network/socks5.py | 8 +++----- src/network/tcp.py | 28 ++++++++++++++++++---------- src/network/udp.py | 1 + 9 files changed, 53 insertions(+), 21 deletions(-) diff --git a/src/network/asyncore_pollchoose.py b/src/network/asyncore_pollchoose.py index 8131b2b2..c212effe 100644 --- a/src/network/asyncore_pollchoose.py +++ b/src/network/asyncore_pollchoose.py @@ -57,7 +57,7 @@ import warnings import os from errno import EALREADY, EINPROGRESS, EWOULDBLOCK, ECONNRESET, EINVAL, \ ENOTCONN, ESHUTDOWN, EISCONN, EBADF, ECONNABORTED, EPIPE, EAGAIN, \ - ECONNREFUSED, EHOSTUNREACH, ENETUNREACH, ENOTSOCK, \ + ECONNREFUSED, EHOSTUNREACH, ENETUNREACH, ENOTSOCK, EINTR, \ errorcode try: from errno import WSAEWOULDBLOCK diff --git a/src/network/bmproto.py b/src/network/bmproto.py index 97c354cd..cd36726e 100644 --- a/src/network/bmproto.py +++ b/src/network/bmproto.py @@ -282,11 +282,11 @@ class BMProto(AdvancedDispatcher, ObjectTracker): except (BMObjectExpiredError, BMObjectUnwantedStreamError): for connection in network.connectionpool.BMConnectionPool().inboundConnections.values() + network.connectionpool.BMConnectionPool().outboundConnections.values(): try: - del connection.objectsNewtoThem[hashId] + del connection.objectsNewtoThem[self.object.inventoryHash] except KeyError: pass try: - del connection.objectsNewToMe[hashId] + del connection.objectsNewToMe[self.object.inventoryHash] except KeyError: pass if not BMConfigParser().get("inventory", "acceptmismatch"): @@ -459,7 +459,10 @@ class BMProto(AdvancedDispatcher, ObjectTracker): def handle_close(self, reason=None): self.set_state("close") if reason is None: - logger.debug("%s:%i: closing", self.destination.host, self.destination.port) + try: + logger.debug("%s:%i: closing", self.destination.host, self.destination.port) + except AttributeError: + logger.debug("Disconnected socket closing") else: logger.debug("%s:%i: closing, %s", self.destination.host, self.destination.port, reason) AdvancedDispatcher.handle_close(self) diff --git a/src/network/connectionpool.py b/src/network/connectionpool.py index 6aa6e49b..1517f4d7 100644 --- a/src/network/connectionpool.py +++ b/src/network/connectionpool.py @@ -7,6 +7,7 @@ import re from bmconfigparser import BMConfigParser from debug import logger import helper_bootstrap +from network.proxy import Proxy import network.bmproto import network.tcp import network.udp @@ -129,6 +130,8 @@ class BMConnectionPool(object): if not self.bootstrapped: helper_bootstrap.dns() self.bootstrapped = True + Proxy.proxy = (BMConfigParser().safeGet("bitmessagesettings", "sockshostname"), + BMConfigParser().safeGetInt("bitmessagesettings", "socksport")) established = sum(1 for c in self.outboundConnections.values() if (c.connected and c.fullyEstablished)) pending = len(self.outboundConnections) - established if established < BMConfigParser().safeGetInt("bitmessagesettings", "maxoutboundconnections"): diff --git a/src/network/objectracker.py b/src/network/objectracker.py index d073d78a..0c4a8d56 100644 --- a/src/network/objectracker.py +++ b/src/network/objectracker.py @@ -54,6 +54,7 @@ class ObjectTracker(object): def clean(self): if self.lastCleaned < time.time() - ObjectTracker.invCleanPeriod: if haveBloom: + # FIXME if PendingDownloadQueue().size() == 0: self.initInvBloom() self.initAddrBloom() diff --git a/src/network/proxy.py b/src/network/proxy.py index e3b5acee..3b7cc848 100644 --- a/src/network/proxy.py +++ b/src/network/proxy.py @@ -1,7 +1,10 @@ import socket +import state + from advanceddispatcher import AdvancedDispatcher import asyncore_pollchoose as asyncore +import network.connectionpool class ProxyError(Exception): pass class GeneralProxyError(ProxyError): pass @@ -32,10 +35,25 @@ class Proxy(AdvancedDispatcher): self.__class__._auth = authTuple def __init__(self, address): - if type(address) != tuple or (len(address) < 2) or (type(str(address[0])) != type('')) or (type(address[1]) != int): + if not isinstance(address, state.Peer): raise ValueError AdvancedDispatcher.__init__(self) self.destination = address + self.isOutbound = True self.create_socket(socket.AF_INET, socket.SOCK_STREAM) self.connect(self.proxy) print "connecting in background to %s:%i" % (self.proxy[0], self.proxy[1]) + + def handle_connect(self): + try: + AdvancedDispatcher.handle_connect(self) + except socket.error as e: + if e.errno in asyncore._DISCONNECTED: + logger.debug("%s:%i: Connection failed: %s" % (self.destination.host, self.destination.port, str(e))) + return + + def state_proxy_handshake_done(self): + self.writeQueue.put(protocol.assembleVersionMessage(self.destination.host, self.destination.port, network.connectionpool.BMConnectionPool().streams, False)) + self.connectedAt = time.time() + return False + diff --git a/src/network/socks4a.py b/src/network/socks4a.py index 4b6b64fa..08bd18cf 100644 --- a/src/network/socks4a.py +++ b/src/network/socks4a.py @@ -43,7 +43,7 @@ class Socks4a(Proxy): self.__proxypeername = (socket.inet_ntoa(self.ipaddr), self.destination[1]) else: self.__proxypeername = (self.destination[0], self.destport) - self.set_state("socks_handshake_done", 8) + self.set_state("proxy_handshake_done", 8) def proxy_sock_name(self): return socket.inet_ntoa(self.__proxysockname[0]) diff --git a/src/network/socks5.py b/src/network/socks5.py index 0d1717d4..6745308c 100644 --- a/src/network/socks5.py +++ b/src/network/socks5.py @@ -4,6 +4,7 @@ import struct from advanceddispatcher import AdvancedDispatcher import asyncore_pollchoose as asyncore from proxy import Proxy, ProxyError, GeneralProxyError +import network.connectionpool class Socks5AuthError(ProxyError): pass class Socks5Error(ProxyError): pass @@ -103,7 +104,7 @@ class Socks5(Proxy): def state_proxy_addr_2_2(self): if not self.read_buf_sufficient(self.address_length): return False - self.boundaddr = read_buf + self.boundaddr = self.read_buf self.set_state("proxy_port", self.address_length) def state_proxy_port(self): @@ -115,14 +116,11 @@ class Socks5(Proxy): self.__proxypeername = (socket.inet_ntoa(self.ipaddr), self.destination[1]) else: self.__proxypeername = (self.destination[0], self.destport) - self.set_state("socks_handshake_done", 2) + self.set_state("proxy_handshake_done", 2) def proxy_sock_name(self): return socket.inet_ntoa(self.__proxysockname[0]) - def state_socks_handshake_done(self): - return False - class Socks5Connection(Socks5): def __init__(self, address): diff --git a/src/network/tcp.py b/src/network/tcp.py index 986a4b63..badb6774 100644 --- a/src/network/tcp.py +++ b/src/network/tcp.py @@ -49,6 +49,10 @@ class TCPConnection(BMProto, TLSDispatcher): TLSDispatcher.__init__(self, sock, server_side=True) self.connectedAt = time.time() logger.debug("Received connection from %s:%i", self.destination.host, self.destination.port) + elif address is not None and sock is not None: + TLSDispatcher.__init__(self, sock, server_side=False) + self.isOutbound = True + logger.debug("Outbound proxy connection to %s:%i", self.destination.host, self.destination.port) else: self.destination = address self.isOutbound = True @@ -159,33 +163,37 @@ class TCPConnection(BMProto, TLSDispatcher): self.connectedAt = time.time() def handle_read(self): - try: - TLSDispatcher.handle_read(self) - except socket.error as e: - logger.debug("%s:%i: Handle read fail: %s" % (self.destination.host, self.destination.port, str(e))) +# try: + TLSDispatcher.handle_read(self) +# except socket.error as e: +# logger.debug("%s:%i: Handle read fail: %s" % (self.destination.host, self.destination.port, str(e))) def handle_write(self): - try: - TLSDispatcher.handle_write(self) - except socket.error as e: - logger.debug("%s:%i: Handle write fail: %s" % (self.destination.host, self.destination.port, str(e))) +# try: + TLSDispatcher.handle_write(self) +# except socket.error as e: +# logger.debug("%s:%i: Handle write fail: %s" % (self.destination.host, self.destination.port, str(e))) class Socks5BMConnection(Socks5Connection, TCPConnection): def __init__(self, address): Socks5Connection.__init__(self, address=address) + TCPConnection.__init__(self, address=address, sock=self.socket) + self.set_state("init") def state_socks_handshake_done(self): - TCPConnection.state_init(self) + self.set_state("bm_header", expectBytes=protocol.Header.size) return False class Socks4aBMConnection(Socks4aConnection, TCPConnection): def __init__(self, address): Socks4aConnection.__init__(self, address=address) + TCPConnection.__init__(self, address=address, sock=self.socket) + self.set_state("init") def state_socks_handshake_done(self): - TCPConnection.state_init(self) + self.set_state("bm_header", expectBytes=protocol.Header.size) return False diff --git a/src/network/udp.py b/src/network/udp.py index 8c710997..241bf9d7 100644 --- a/src/network/udp.py +++ b/src/network/udp.py @@ -3,6 +3,7 @@ from binascii import hexlify import hashlib import math import time +import Queue import socket import struct import random From a3a55e53c4e4c4e1b3bf2125b781e01e76a0786b Mon Sep 17 00:00:00 2001 From: Peter Surda Date: Sun, 11 Jun 2017 14:11:39 +0200 Subject: [PATCH 102/407] UDP Socket dict address fix - fixes #1008 --- src/network/connectionpool.py | 4 ++-- src/network/udp.py | 1 + 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/src/network/connectionpool.py b/src/network/connectionpool.py index 1517f4d7..41fb97be 100644 --- a/src/network/connectionpool.py +++ b/src/network/connectionpool.py @@ -69,7 +69,7 @@ class BMConnectionPool(object): def removeConnection(self, connection): if isinstance(connection, network.udp.UDPSocket): - del self.udpSockets[connection.destination.host] + del self.udpSockets[connection.listening.host] if isinstance(connection, network.tcp.TCPServer): del self.listeningSockets[state.Peer(connection.destination.host, connection.destination.port)] elif connection.isOutbound: @@ -109,7 +109,7 @@ class BMConnectionPool(object): udpSocket = network.udp.UDPSocket(host=host) else: udpSocket = network.udp.UDPSocket(host=bind) - self.udpSockets[udpSocket.destination.host] = udpSocket + self.udpSockets[udpSocket.listening.host] = udpSocket def loop(self): # defaults to empty loop if outbound connections are maxed diff --git a/src/network/udp.py b/src/network/udp.py index 241bf9d7..9eccbf67 100644 --- a/src/network/udp.py +++ b/src/network/udp.py @@ -65,6 +65,7 @@ class UDPSocket(BMProto): self.socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEPORT, 1) except AttributeError: pass + self.listening = state.Peer(self.socket.getsockname()[0], self.socket.getsockname()[1]) self.destination = state.Peer(self.socket.getsockname()[0], self.socket.getsockname()[1]) ObjectTracker.__init__(self) self.connecting = False From b7f9e74eeaec252fa0d5acea20d5452c982dbe8a Mon Sep 17 00:00:00 2001 From: Dmitri Bogomolov <4glitch@gmail.com> Date: Mon, 12 Jun 2017 12:52:24 +0300 Subject: [PATCH 103/407] setup.py: added new packages appeared after 36b5e2c --- setup.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/setup.py b/setup.py index a04c51d4..8a51368d 100644 --- a/setup.py +++ b/setup.py @@ -242,6 +242,9 @@ if __name__ == "__main__": 'pybitmessage.network', 'pybitmessage.pyelliptic', 'pybitmessage.socks', + 'pybitmessage.storage', + 'pybitmessage.fallback', + 'pybitmessage.fallback.umsgpack', 'pybitmessage.plugins' ], package_data={'': [ From 06fed6f9c27af40f9b078fbb5dd71399d37ed268 Mon Sep 17 00:00:00 2001 From: Dmitri Bogomolov <4glitch@gmail.com> Date: Mon, 12 Jun 2017 12:58:16 +0300 Subject: [PATCH 104/407] setup.py: second exception in list can be treated as variable --- setup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.py b/setup.py index 8a51368d..5330ae68 100644 --- a/setup.py +++ b/setup.py @@ -184,7 +184,7 @@ class InstallCmd(install): print "Press Return to continue" try: raw_input() - except EOFError, NameError: + except (EOFError, NameError): pass return install.run(self) From 76fed7821179f8df279148ed35e49950c6442f3f Mon Sep 17 00:00:00 2001 From: Dmitri Bogomolov <4glitch@gmail.com> Date: Mon, 12 Jun 2017 13:54:44 +0300 Subject: [PATCH 105/407] Made it possible to use installed umsgpack --- setup.py | 49 ++++++++++++++++++++++++++++------------- src/depends.py | 5 +++-- src/helper_msgcoding.py | 7 ++++-- 3 files changed, 42 insertions(+), 19 deletions(-) diff --git a/setup.py b/setup.py index 5330ae68..0da5680a 100644 --- a/setup.py +++ b/setup.py @@ -52,7 +52,22 @@ packageName = { "openSUSE": "python-msgpack-python", "Fedora": "python2-msgpack", "Guix": "python2-msgpack", - "Gentoo": "dev-python/msgpack" + "Gentoo": "dev-python/msgpack", + "optional": True, + "description": "python-msgpack is recommended for messages coding" + }, + "umsgpack": { + "FreeBSD": "", + "OpenBSD": "", + "Fedora": "", + "openSUSE": "", + "Guix": "", + "Ubuntu 12": "", + "Debian": "python-u-msgpack", + "Ubuntu": "python-u-msgpack", + "Gentoo": "dev-python/u-msgpack", + "optional": True, + "description": "umsgpack can be used instead of msgpack" }, "pyopencl": { "FreeBSD": "py27-pyopencl", @@ -202,9 +217,25 @@ if __name__ == "__main__": ) installRequires = [] - # this will silently accept alternative providers of msgpack if they are already installed + packages = [ + 'pybitmessage', + 'pybitmessage.bitmessageqt', + 'pybitmessage.bitmessagecurses', + 'pybitmessage.messagetypes', + 'pybitmessage.network', + 'pybitmessage.pyelliptic', + 'pybitmessage.socks', + 'pybitmessage.storage', + 'pybitmessage.plugins' + ] + # this will silently accept alternative providers of msgpack + # if they are already installed if "msgpack" in detectPrereqs(): installRequires.append("msgpack-python") + elif "umsgpack" in detectPrereqs(): + installRequires.append("umsgpack") + else: + packages += ['pybitmessage.fallback', 'pybitmessage.fallback.umsgpack'] try: dist = setup( @@ -234,19 +265,7 @@ if __name__ == "__main__": "Topic :: Software Development :: Libraries :: Python Modules", ], package_dir={'pybitmessage': 'src'}, - packages=[ - 'pybitmessage', - 'pybitmessage.bitmessageqt', - 'pybitmessage.bitmessagecurses', - 'pybitmessage.messagetypes', - 'pybitmessage.network', - 'pybitmessage.pyelliptic', - 'pybitmessage.socks', - 'pybitmessage.storage', - 'pybitmessage.fallback', - 'pybitmessage.fallback.umsgpack', - 'pybitmessage.plugins' - ], + packages=packages, package_data={'': [ 'bitmessageqt/*.ui', 'bitmsghash/*.cl', 'sslkeys/*.pem', 'translations/*.ts', 'translations/*.qm', diff --git a/src/depends.py b/src/depends.py index 49c5c7a8..d66663b1 100755 --- a/src/depends.py +++ b/src/depends.py @@ -204,7 +204,9 @@ def check_msgpack(): try: import msgpack except ImportError: - logger.error('The msgpack package is not available. PyBitmessage requires msgpack.') + logger.error( + 'The msgpack package is not available.' + 'It is highly recommended for messages coding.') if sys.platform.startswith('openbsd'): logger.error('On OpenBSD, try running "pkg_add py-msgpack" as root.') elif sys.platform.startswith('freebsd'): @@ -224,7 +226,6 @@ def check_msgpack(): else: logger.error('If your package manager does not have this package, try running "pip install msgpack-python".') - return False return True def check_dependencies(verbose = False, optional = False): diff --git a/src/helper_msgcoding.py b/src/helper_msgcoding.py index 3e156754..aae35d27 100644 --- a/src/helper_msgcoding.py +++ b/src/helper_msgcoding.py @@ -3,12 +3,14 @@ try: import msgpack except ImportError: - import fallback.umsgpack.umsgpack as msgpack + try: + import umsgpack as msgpack + except ImportError: + import fallback.umsgpack.umsgpack as msgpack import string import zlib from bmconfigparser import BMConfigParser -import shared from debug import logger import messagetypes from tr import _translate @@ -18,6 +20,7 @@ BITMESSAGE_ENCODING_TRIVIAL = 1 BITMESSAGE_ENCODING_SIMPLE = 2 BITMESSAGE_ENCODING_EXTENDED = 3 + class DecompressionSizeException(Exception): def __init__(self, size): self.size = size From 0cc8589b27e1b91c0c8eb0356efe70d1f3ada575 Mon Sep 17 00:00:00 2001 From: Peter Surda Date: Wed, 21 Jun 2017 12:16:33 +0200 Subject: [PATCH 106/407] Asyncore updates - should prevent the same object being re-requested indefinitely - locking for object tracking - move SSL-specific error handling to TLSDispatcher - observe maximum connection limit when accepting a new connection - stack depth test (for debugging purposes) - separate download thread - connection pool init moved to main thread --- src/bitmessagemain.py | 6 +++- src/network/advanceddispatcher.py | 8 ++++- src/network/announcethread.py | 1 - src/network/asyncore_pollchoose.py | 21 +++++-------- src/network/bmproto.py | 21 ++++++------- src/network/downloadthread.py | 48 ++++++++++++++++++++++++++++++ src/network/invthread.py | 9 +++--- src/network/networkthread.py | 1 - src/network/objectracker.py | 17 +++++------ src/network/receivequeuethread.py | 15 +++++++--- src/network/tcp.py | 6 ++++ src/network/tls.py | 18 +++++++++++ 12 files changed, 125 insertions(+), 46 deletions(-) create mode 100644 src/network/downloadthread.py diff --git a/src/bitmessagemain.py b/src/bitmessagemain.py index 57eab27a..d9578748 100755 --- a/src/bitmessagemain.py +++ b/src/bitmessagemain.py @@ -58,7 +58,7 @@ from network.networkthread import BMNetworkThread from network.receivequeuethread import ReceiveQueueThread from network.announcethread import AnnounceThread from network.invthread import InvThread -#from network.downloadthread import DownloadThread +from network.downloadthread import DownloadThread # Helper Functions import helper_bootstrap @@ -261,6 +261,7 @@ class Main: singleAPIThread.start() if BMConfigParser().get("network", "asyncore"): + BMConnectionPool() asyncoreThread = BMNetworkThread() asyncoreThread.daemon = True asyncoreThread.start() @@ -273,6 +274,9 @@ class Main: state.invThread = InvThread() state.invThread.daemon = True state.invThread.start() + downloadThread = DownloadThread() + downloadThread.daemon = True + downloadThread.start() connectToStream(1) diff --git a/src/network/advanceddispatcher.py b/src/network/advanceddispatcher.py index dadb625b..80762aa5 100644 --- a/src/network/advanceddispatcher.py +++ b/src/network/advanceddispatcher.py @@ -1,5 +1,6 @@ -import socket import Queue +import socket +import sys import time import asyncore_pollchoose as asyncore @@ -42,6 +43,11 @@ class AdvancedDispatcher(asyncore.dispatcher): if not self.connected: return maxLoop = 20 + try: + sys._getframe(200) + logger.error("Stack depth warning") + except ValueError: + pass while maxLoop > 0: try: # print "Trying to handle state \"%s\"" % (self.state) diff --git a/src/network/announcethread.py b/src/network/announcethread.py index 85e69877..37b7e628 100644 --- a/src/network/announcethread.py +++ b/src/network/announcethread.py @@ -16,7 +16,6 @@ class AnnounceThread(threading.Thread, StoppableThread): threading.Thread.__init__(self, name="AnnounceThread") self.initStop() self.name = "AnnounceThread" - BMConnectionPool() logger.info("init announce thread") def run(self): diff --git a/src/network/asyncore_pollchoose.py b/src/network/asyncore_pollchoose.py index c212effe..94cbfaf3 100644 --- a/src/network/asyncore_pollchoose.py +++ b/src/network/asyncore_pollchoose.py @@ -57,7 +57,7 @@ import warnings import os from errno import EALREADY, EINPROGRESS, EWOULDBLOCK, ECONNRESET, EINVAL, \ ENOTCONN, ESHUTDOWN, EISCONN, EBADF, ECONNABORTED, EPIPE, EAGAIN, \ - ECONNREFUSED, EHOSTUNREACH, ENETUNREACH, ENOTSOCK, EINTR, \ + ECONNREFUSED, EHOSTUNREACH, ENETUNREACH, ENOTSOCK, EINTR, ETIMEDOUT, \ errorcode try: from errno import WSAEWOULDBLOCK @@ -68,10 +68,8 @@ try: except (ImportError, AttributeError): WSAENOTSOCK = ENOTSOCK -from ssl import SSLError, SSL_ERROR_WANT_READ, SSL_ERROR_WANT_WRITE - _DISCONNECTED = frozenset((ECONNRESET, ENOTCONN, ESHUTDOWN, ECONNABORTED, EPIPE, - EBADF, ECONNREFUSED, EHOSTUNREACH, ENETUNREACH)) + EBADF, ECONNREFUSED, EHOSTUNREACH, ENETUNREACH, ETIMEDOUT)) OP_READ = 1 OP_WRITE = 2 @@ -563,11 +561,6 @@ class dispatcher: try: result = self.socket.send(data) return result - except SSLError as err: - if err.errno == SSL_ERROR_WANT_WRITE: - return 0 - else: - raise except socket.error as why: if why.args[0] in (EAGAIN, EWOULDBLOCK, WSAEWOULDBLOCK): return 0 @@ -587,11 +580,6 @@ class dispatcher: return b'' else: return data - except SSLError as err: - if err.errno == SSL_ERROR_WANT_READ: - return b'' - else: - raise except socket.error as why: # winsock sometimes raises ENOTCONN if why.args[0] in (EAGAIN, EWOULDBLOCK, WSAEWOULDBLOCK): @@ -616,6 +604,11 @@ class dispatcher: # cheap inheritance, used to pass all other attribute # references to the underlying socket object. def __getattr__(self, attr): + try: + sys._getframe(200) + logger.error("Stack depth warning") + except ValueError: + pass try: retattr = getattr(self.socket, attr) except AttributeError: diff --git a/src/network/bmproto.py b/src/network/bmproto.py index cd36726e..39464845 100644 --- a/src/network/bmproto.py +++ b/src/network/bmproto.py @@ -119,7 +119,7 @@ class BMProto(AdvancedDispatcher, ObjectTracker): except BMObjectInvalidError: logger.debug("object invalid, skipping") except BMObjectAlreadyHaveError: - logger.debug("already got object, skipping") + logger.debug("%s:%i already got object, skipping", self.destination.host, self.destination.port) except struct.error: logger.debug("decoding error, skipping") else: @@ -260,10 +260,7 @@ class BMProto(AdvancedDispatcher, ObjectTracker): for i in items: self.receiveQueue.put(("inv", i)) - self.handleReceivedInventory(i) - payload = addresses.encodeVarint(len(self.objectsNewToMe)) + ''.join(self.objectsNewToMe.keys()) - self.writeQueue.put(protocol.CreatePacket('getdata', payload)) return True def bm_command_object(self): @@ -279,19 +276,23 @@ class BMProto(AdvancedDispatcher, ObjectTracker): try: self.object.checkEOLSanity() self.object.checkStream() - except (BMObjectExpiredError, BMObjectUnwantedStreamError): + self.object.checkAlreadyHave() + except (BMObjectExpiredError, BMObjectUnwantedStreamError, BMObjectAlreadyHaveError) as e: for connection in network.connectionpool.BMConnectionPool().inboundConnections.values() + network.connectionpool.BMConnectionPool().outboundConnections.values(): try: - del connection.objectsNewtoThem[self.object.inventoryHash] + with connection.objectsNewToThemLock: + del connection.objectsNewToThem[self.object.inventoryHash] except KeyError: pass try: - del connection.objectsNewToMe[self.object.inventoryHash] + with connection.objectsNewToMeLock: + del connection.objectsNewToMe[self.object.inventoryHash] except KeyError: pass - if not BMConfigParser().get("inventory", "acceptmismatch"): - raise - self.object.checkAlreadyHave() + if not BMConfigParser().get("inventory", "acceptmismatch") or \ + isinstance(e, BMObjectAlreadyHaveError) or \ + isinstance(e, BMObjectExpiredError): + raise e if self.object.objectType == protocol.OBJECT_GETPUBKEY: self.object.checkGetpubkey() diff --git a/src/network/downloadthread.py b/src/network/downloadthread.py new file mode 100644 index 00000000..e8bd44a7 --- /dev/null +++ b/src/network/downloadthread.py @@ -0,0 +1,48 @@ +import Queue +import threading + +import addresses +#from bmconfigparser import BMConfigParser +from debug import logger +from helper_threading import StoppableThread +#from inventory import Inventory +from network.connectionpool import BMConnectionPool +import protocol + +class DownloadThread(threading.Thread, StoppableThread): + maxPending = 500 + requestChunk = 1000 + + def __init__(self): + threading.Thread.__init__(self, name="DownloadThread") + self.initStop() + self.name = "DownloadThread" + logger.info("init download thread") + + def run(self): + while not self._stopped: + requested = 0 + for i in BMConnectionPool().inboundConnections.values() + BMConnectionPool().outboundConnections.values(): + # this may take a while, but it needs a consistency so I think it's better + with i.objectsNewToMeLock: + downloadPending = len(list((k for k, v in i.objectsNewToMe.iteritems() if not v))) + if downloadPending >= DownloadThread.maxPending: + continue + # keys with True values in the dict + request = list((k for k, v in i.objectsNewToMe.iteritems() if v)) + if len(request) == 0: + continue + if len(request) > DownloadThread.requestChunk - downloadPending: + request = request[:DownloadThread.requestChunk - downloadPending] + # mark them as pending + for k in request: + i.objectsNewToMe[k] = False + + payload = addresses.encodeVarint(len(request)) + ''.join(request) + i.writeQueue.put(protocol.CreatePacket('getdata', payload)) + logger.debug("%s:%i Requesting %i objects", i.destination.host, i.destination.port, len(request)) + requested += len(request) + self.stop.wait(1) + + def stopThread(self): + super(DownloadThread, self).stopThread() diff --git a/src/network/invthread.py b/src/network/invthread.py index 6d1828e1..63107a1f 100644 --- a/src/network/invthread.py +++ b/src/network/invthread.py @@ -40,7 +40,6 @@ class InvThread(threading.Thread, StoppableThread): else: BMConnectionPool().handleReceivedObject(data[0], data[1], data[2]) self.holdHash (data[0], data[1]) - #print "Holding hash %i, %s" % (stream, hexlify(hash)) except Queue.Empty: break @@ -50,13 +49,15 @@ class InvThread(threading.Thread, StoppableThread): for stream in connection.streams: try: for hashId in self.collectionOfInvs[iterator][stream]: - if hashId in connection.objectsNewToThem: + try: + with connection.objectsNewToThemLock: + del connection.objectsNewToThem[hashId] hashes.append(hashId) - del connection.objectsNewToThem[hashId] + except KeyError: + pass except KeyError: continue if len(hashes) > 0: - #print "sending inv of %i" % (len(hashes)) connection.writeQueue.put(protocol.CreatePacket('inv', addresses.encodeVarint(len(hashes)) + "".join(hashes))) self.collectionOfInvs[iterator] = {} iterator += 1 diff --git a/src/network/networkthread.py b/src/network/networkthread.py index 7e98bcc0..a4a23103 100644 --- a/src/network/networkthread.py +++ b/src/network/networkthread.py @@ -12,7 +12,6 @@ class BMNetworkThread(threading.Thread, StoppableThread): threading.Thread.__init__(self, name="AsyncoreThread") self.initStop() self.name = "AsyncoreThread" - BMConnectionPool() logger.info("init asyncore thread") def run(self): diff --git a/src/network/objectracker.py b/src/network/objectracker.py index 0c4a8d56..5c0ad147 100644 --- a/src/network/objectracker.py +++ b/src/network/objectracker.py @@ -2,6 +2,7 @@ from Queue import Queue import time from threading import RLock +from debug import logger from inventory import Inventory from network.downloadqueue import DownloadQueue from network.uploadqueue import UploadQueue @@ -33,8 +34,6 @@ class ObjectTracker(object): self.objectsNewToMeLock = RLock() self.objectsNewToThem = {} self.objectsNewToThemLock = RLock() - self.downloadPending = 0 - self.downloadQueue = Queue() self.initInvBloom() self.initAddrBloom() self.lastCleaned = time.time() @@ -77,16 +76,14 @@ class ObjectTracker(object): def handleReceivedInventory(self, hashId): if haveBloom: self.invBloom.add(hashId) - elif hashId in Inventory(): - try: - with self.objectsNewToThemLock: - del self.objectsNewToThem[hashId] - except KeyError: - pass - else: + try: + with self.objectsNewToThemLock: + del self.objectsNewToThem[hashId] + except KeyError: + pass + if hashId not in Inventory(): with self.objectsNewToMeLock: self.objectsNewToMe[hashId] = True -# self.DownloadQueue.put(hashId) def hasAddr(self, addr): if haveBloom: diff --git a/src/network/receivequeuethread.py b/src/network/receivequeuethread.py index b31b82b4..6662a078 100644 --- a/src/network/receivequeuethread.py +++ b/src/network/receivequeuethread.py @@ -1,4 +1,5 @@ import Queue +import sys import threading import time @@ -17,7 +18,6 @@ class ReceiveQueueThread(threading.Thread, StoppableThread): threading.Thread.__init__(self, name="ReceiveQueueThread") self.initStop() self.name = "ReceiveQueueThread" - BMConnectionPool() logger.info("init receive queue thread") def run(self): @@ -25,6 +25,11 @@ class ReceiveQueueThread(threading.Thread, StoppableThread): while not self._stopped and state.shutdown == 0: if lastprinted < int(time.time()): lastprinted = int(time.time()) + try: + sys._getframe(200) + logger.error("Stack depth warning") + except ValueError: + pass processed = 0 for i in BMConnectionPool().inboundConnections.values() + BMConnectionPool().outboundConnections.values(): if self._stopped: @@ -61,9 +66,11 @@ class ReceiveQueueThread(threading.Thread, StoppableThread): # Select all hashes for objects in this stream. bigInvList = {} for stream in connection.streams: - for objHash in Inventory().unexpired_hashes_by_stream(stream): - bigInvList[objHash] = 0 - connection.objectsNewToThem[objHash] = True + # may lock for a long time, but I think it's better than thousands of small locks + with connection.objectsNewToThemLock: + for objHash in Inventory().unexpired_hashes_by_stream(stream): + bigInvList[objHash] = 0 + connection.objectsNewToThem[objHash] = True objectCount = 0 payload = b'' # Now let us start appending all of these hashes together. They will be diff --git a/src/network/tcp.py b/src/network/tcp.py index badb6774..8a6b5705 100644 --- a/src/network/tcp.py +++ b/src/network/tcp.py @@ -212,6 +212,12 @@ class TCPServer(AdvancedDispatcher): if pair is not None: sock, addr = pair state.ownAddresses[state.Peer(sock.getsockname()[0], sock.getsockname()[1])] = True + if len(network.connectionpool.BMConnectionPool().inboundConnections) + \ + len(network.connectionpool.BMConnectionPool().outboundConnections) > \ + BMConfigParser().safeGetInt("bitmessagesettings", "maxtotalconnections") + \ + BMConfigParser().safeGetInt("bitmessagesettings", "maxbootstrapconnections"): + close(sock) + return try: network.connectionpool.BMConnectionPool().addConnection(TCPConnection(sock=sock)) except socket.error: diff --git a/src/network/tls.py b/src/network/tls.py index 115f3faa..9694b4b9 100644 --- a/src/network/tls.py +++ b/src/network/tls.py @@ -13,6 +13,8 @@ import network.asyncore_pollchoose as asyncore import paths import protocol +_DISCONNECTED_SSL = frozenset((ssl.SSL_ERROR_EOF,)) + class TLSDispatcher(AdvancedDispatcher): def __init__(self, address=None, sock=None, certfile=None, keyfile=None, server_side=False, ciphers=protocol.sslProtocolCiphers): @@ -90,6 +92,14 @@ class TLSDispatcher(AdvancedDispatcher): return AdvancedDispatcher.handle_read(self) except AttributeError: return AdvancedDispatcher.handle_read(self) + except ssl.SSLError as err: + if err.errno == ssl.SSL_ERROR_WANT_READ: + return + elif err.errno in _DISCONNECTED_SSL: + self.handle_close() + return + else: + raise def handle_write(self): try: @@ -102,6 +112,14 @@ class TLSDispatcher(AdvancedDispatcher): return AdvancedDispatcher.handle_write(self) except AttributeError: return AdvancedDispatcher.handle_read(self) + except ssl.SSLError as err: + if err.errno == ssl.SSL_ERROR_WANT_WRITE: + return 0 + elif err.errno in _DISCONNECTED_SSL: + self.handle_close() + return 0 + else: + raise def tls_handshake(self): # wait for flush From 618f3865c1e456a422ae37e653bfe0f95a51c320 Mon Sep 17 00:00:00 2001 From: Peter Surda Date: Wed, 21 Jun 2017 12:16:56 +0200 Subject: [PATCH 107/407] Main thread end - instead of sleeping, it just ends --- src/bitmessagemain.py | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/bitmessagemain.py b/src/bitmessagemain.py index d9578748..40ccdee3 100755 --- a/src/bitmessagemain.py +++ b/src/bitmessagemain.py @@ -309,9 +309,6 @@ class Main: else: BMConfigParser().remove_option('bitmessagesettings', 'dontconnect') - while True: - time.sleep(20) - def daemonize(self): if os.fork(): exit(0) From 243025a1aa6aff8d3dde4a4abb7f201249f2d38a Mon Sep 17 00:00:00 2001 From: Peter Surda Date: Wed, 21 Jun 2017 12:17:40 +0200 Subject: [PATCH 108/407] Leave __delitem__ unimplemented in filesystem storage - rename the cleaning method --- src/storage/filesystem.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/storage/filesystem.py b/src/storage/filesystem.py index 4efe1132..d64894a9 100644 --- a/src/storage/filesystem.py +++ b/src/storage/filesystem.py @@ -65,7 +65,7 @@ class FilesystemInventory(InventoryStorage): self._inventory[value.stream] = {} self._inventory[value.stream][hash] = value - def __delitem__(self, hash): + def delHashId(self, hash): for stream in self._inventory.keys(): try: del self._inventory[stream][hash] @@ -172,4 +172,4 @@ class FilesystemInventory(InventoryStorage): if item.expires < minTime: deletes.append(hashId) for hashId in deletes: - del self[hashId] + self.delHashId(hashId) From 2685fe29b19bbf6d5a75a0d640d2baef173f18f6 Mon Sep 17 00:00:00 2001 From: Peter Surda Date: Sat, 24 Jun 2017 12:13:35 +0200 Subject: [PATCH 109/407] Code quality improvements --- src/bitmessagemain.py | 1 - src/bitmessageqt/safehtmlparser.py | 6 +++--- src/bmconfigparser.py | 13 ++++++------- src/network/advanceddispatcher.py | 8 +++----- src/network/announcethread.py | 5 ----- src/network/asyncore_pollchoose.py | 5 ----- src/network/bmobject.py | 6 +++--- src/network/bmproto.py | 26 +++++++++++--------------- src/network/connectionchooser.py | 17 ++++++++--------- src/network/connectionpool.py | 1 - src/network/downloadthread.py | 6 +----- src/network/invthread.py | 16 ++++++++-------- src/network/stats.py | 23 ++++++++--------------- src/network/tls.py | 18 ++++++++---------- src/network/udp.py | 25 ++++--------------------- src/storage/sqlite.py | 2 +- src/storage/storage.py | 2 +- 17 files changed, 65 insertions(+), 115 deletions(-) diff --git a/src/bitmessagemain.py b/src/bitmessagemain.py index 40ccdee3..a2d951ed 100755 --- a/src/bitmessagemain.py +++ b/src/bitmessagemain.py @@ -27,7 +27,6 @@ import socket import ctypes from struct import pack from subprocess import call -import time from api import MySimpleXMLRPCRequestHandler, StoppableXMLRPCServer from helper_startup import isOurOperatingSystemLimitedToHavingVeryFewHalfOpenConnections diff --git a/src/bitmessageqt/safehtmlparser.py b/src/bitmessageqt/safehtmlparser.py index a78991d3..88431855 100644 --- a/src/bitmessageqt/safehtmlparser.py +++ b/src/bitmessageqt/safehtmlparser.py @@ -53,20 +53,20 @@ class SafeHTMLParser(HTMLParser): self.allow_external_src = False def add_if_acceptable(self, tag, attrs = None): - if not tag in SafeHTMLParser.acceptable_elements: + if tag not in SafeHTMLParser.acceptable_elements: return self.sanitised += "<" if inspect.stack()[1][3] == "handle_endtag": self.sanitised += "/" self.sanitised += tag - if not attrs is None: + if attrs is not None: for attr, val in attrs: if tag == "img" and attr == "src" and not self.allow_picture: val = "" elif attr == "src" and not self.allow_external_src: url = urlparse(val) if url.scheme not in SafeHTMLParser.src_schemes: - val == "" + val = "" self.sanitised += " " + quote_plus(attr) if not (val is None): self.sanitised += "=\"" + val + "\"" diff --git a/src/bmconfigparser.py b/src/bmconfigparser.py index bfa7e396..0db58103 100644 --- a/src/bmconfigparser.py +++ b/src/bmconfigparser.py @@ -36,14 +36,13 @@ class BMConfigParser(ConfigParser.SafeConfigParser): raise TypeError("option values must be strings") return ConfigParser.ConfigParser.set(self, section, option, value) - def get(self, section, option, raw=False, vars=None): + def get(self, section, option, raw=False, variables=None): try: if section == "bitmessagesettings" and option == "timeformat": - return ConfigParser.ConfigParser.get(self, section, option, raw, vars) - else: - return ConfigParser.ConfigParser.get(self, section, option, True, vars) + return ConfigParser.ConfigParser.get(self, section, option, raw, variables) + return ConfigParser.ConfigParser.get(self, section, option, True, variables) except ConfigParser.InterpolationError: - return ConfigParser.ConfigParser.get(self, section, option, True, vars) + return ConfigParser.ConfigParser.get(self, section, option, True, variables) except (ConfigParser.NoSectionError, ConfigParser.NoOptionError) as e: try: return BMConfigDefaults[section][option] @@ -68,8 +67,8 @@ class BMConfigParser(ConfigParser.SafeConfigParser): except (ConfigParser.NoSectionError, ConfigParser.NoOptionError, ValueError, AttributeError): return default - def items(self, section, raw=False, vars=None): - return ConfigParser.ConfigParser.items(self, section, True, vars) + def items(self, section, raw=False, variables=None): + return ConfigParser.ConfigParser.items(self, section, True, variables) def addresses(self): return filter(lambda x: x.startswith('BM-'), BMConfigParser().sections()) diff --git a/src/network/advanceddispatcher.py b/src/network/advanceddispatcher.py index 80762aa5..61dd197b 100644 --- a/src/network/advanceddispatcher.py +++ b/src/network/advanceddispatcher.py @@ -5,7 +5,6 @@ import time import asyncore_pollchoose as asyncore from debug import logger -from bmconfigparser import BMConfigParser class AdvancedDispatcher(asyncore.dispatcher): _buf_len = 2097152 # 2MB @@ -34,11 +33,10 @@ class AdvancedDispatcher(asyncore.dispatcher): def read_buf_sufficient(self, length=0): if len(self.read_buf) < length: return False - else: - return True + return True def process(self): - if self.state != "tls_handshake" and len(self.read_buf) == 0: + if self.state != "tls_handshake" and not self.read_buf: return if not self.connected: return @@ -100,7 +98,7 @@ class AdvancedDispatcher(asyncore.dispatcher): break if bufSize <= 0: return - if len(self.write_buf) > 0: + if self.write_buf: written = self.send(self.write_buf[0:bufSize]) asyncore.update_sent(written) self.sentBytes += written diff --git a/src/network/announcethread.py b/src/network/announcethread.py index 37b7e628..3adcae48 100644 --- a/src/network/announcethread.py +++ b/src/network/announcethread.py @@ -1,4 +1,3 @@ -import Queue import threading import time @@ -8,7 +7,6 @@ from helper_threading import StoppableThread from network.bmproto import BMProto from network.connectionpool import BMConnectionPool from network.udp import UDPSocket -import protocol import state class AnnounceThread(threading.Thread, StoppableThread): @@ -33,6 +31,3 @@ class AnnounceThread(threading.Thread, StoppableThread): for stream in state.streamsInWhichIAmParticipating: addr = (stream, state.Peer('127.0.0.1', BMConfigParser().safeGetInt("bitmessagesettings", "port")), time.time()) connection.writeQueue.put(BMProto.assembleAddr([addr])) - - def stopThread(self): - super(AnnounceThread, self).stopThread() diff --git a/src/network/asyncore_pollchoose.py b/src/network/asyncore_pollchoose.py index 94cbfaf3..3f188812 100644 --- a/src/network/asyncore_pollchoose.py +++ b/src/network/asyncore_pollchoose.py @@ -604,11 +604,6 @@ class dispatcher: # cheap inheritance, used to pass all other attribute # references to the underlying socket object. def __getattr__(self, attr): - try: - sys._getframe(200) - logger.error("Stack depth warning") - except ValueError: - pass try: retattr = getattr(self.socket, attr) except AttributeError: diff --git a/src/network/bmobject.py b/src/network/bmobject.py index e16a6937..318cbfd1 100644 --- a/src/network/bmobject.py +++ b/src/network/bmobject.py @@ -51,18 +51,18 @@ class BMObject(object): def checkEOLSanity(self): # EOL sanity check if self.expiresTime - int(time.time()) > BMObject.maxTTL: - logger.info('This object\'s End of Life time is too far in the future. Ignoring it. Time is %s' % self.expiresTime) + logger.info('This object\'s End of Life time is too far in the future. Ignoring it. Time is %i', self.expiresTime) # TODO: remove from download queue raise BMObjectExpiredError() if self.expiresTime - int(time.time()) < BMObject.minTTL: - logger.info('This object\'s End of Life time was too long ago. Ignoring the object. Time is %s' % self.expiresTime) + logger.info('This object\'s End of Life time was too long ago. Ignoring the object. Time is %i', self.expiresTime) # TODO: remove from download queue raise BMObjectExpiredError() def checkStream(self): if self.streamNumber not in state.streamsInWhichIAmParticipating: - logger.debug('The streamNumber %s isn\'t one we are interested in.' % self.streamNumber) + logger.debug('The streamNumber %i isn\'t one we are interested in.', self.streamNumber) raise BMObjectUnwantedStreamError() def checkAlreadyHave(self): diff --git a/src/network/bmproto.py b/src/network/bmproto.py index 39464845..32bf038c 100644 --- a/src/network/bmproto.py +++ b/src/network/bmproto.py @@ -15,14 +15,12 @@ from network.bmobject import BMObject, BMObjectInsufficientPOWError, BMObjectInv import network.connectionpool from network.downloadqueue import DownloadQueue from network.node import Node -import network.asyncore_pollchoose as asyncore from network.objectracker import ObjectTracker from network.proxy import Proxy, ProxyError, GeneralProxyError -from network.uploadqueue import UploadQueue, UploadElem, AddrUploadQueue, ObjUploadQueue import addresses from bmconfigparser import BMConfigParser -from queues import objectProcessorQueue, portCheckerQueue, UISignalQueue, invQueue +from queues import objectProcessorQueue, portCheckerQueue, invQueue import shared import state import protocol @@ -173,7 +171,6 @@ class BMProto(AdvancedDispatcher, ObjectTracker): retval = [] size = None - insideDigit = False i = 0 while i < len(pattern): @@ -353,14 +350,13 @@ class BMProto(AdvancedDispatcher, ObjectTracker): self.set_state("tls_init", self.payloadLength) self.bm_proto_reset() return False - else: - self.set_connection_fully_established() - return True + self.set_connection_fully_established() + return True return True def bm_command_version(self): - #self.remoteProtocolVersion, self.services, self.timestamp, padding1, self.myExternalIP, padding2, self.remoteNodeIncomingPort = protocol.VersionPacket.unpack(self.payload[:protocol.VersionPacket.size]) - self.remoteProtocolVersion, self.services, self.timestamp, self.sockNode, self.peerNode, self.nonce, self.userAgent, self.streams = self.decode_payload_content("IQQiiQlslv") + self.remoteProtocolVersion, self.services, self.timestamp, self.sockNode, self.peerNode, self.nonce, \ + self.userAgent, self.streams = self.decode_payload_content("IQQiiQlslv") self.nonce = struct.pack('>Q', self.nonce) self.timeOffset = self.timestamp - int(time.time()) logger.debug("remoteProtocolVersion: %i", self.remoteProtocolVersion) @@ -377,7 +373,8 @@ class BMProto(AdvancedDispatcher, ObjectTracker): self.writeQueue.put(protocol.CreatePacket('verack')) self.verackSent = True if not self.isOutbound: - self.writeQueue.put(protocol.assembleVersionMessage(self.destination.host, self.destination.port, network.connectionpool.BMConnectionPool().streams, True)) + self.writeQueue.put(protocol.assembleVersionMessage(self.destination.host, self.destination.port, \ + network.connectionpool.BMConnectionPool().streams, True)) #print "%s:%i: Sending version" % (self.destination.host, self.destination.port) if ((self.services & protocol.NODE_SSL == protocol.NODE_SSL) and protocol.haveSSL(not self.isOutbound)): @@ -387,9 +384,8 @@ class BMProto(AdvancedDispatcher, ObjectTracker): self.set_state("tls_init", self.payloadLength) self.bm_proto_reset() return False - else: - self.set_connection_fully_established() - return True + self.set_connection_fully_established() + return True return True def peerValidityChecks(self): @@ -415,7 +411,7 @@ class BMProto(AdvancedDispatcher, ObjectTracker): return False else: shared.timeOffsetWrongCount = 0 - if len(self.streams) == 0: + if not self.streams: self.writeQueue.put(protocol.assembleErrorMessage(fatal=2, errorText="We don't have shared stream interests. Closing connection.")) logger.debug ('Closed connection to %s because there is no overlapping interest in streams.', @@ -442,7 +438,7 @@ class BMProto(AdvancedDispatcher, ObjectTracker): @staticmethod def assembleAddr(peerList): - if type(peerList) is state.Peer: + if isinstance(peerList, state.Peer): peerList = (peerList) # TODO handle max length, now it's done by upper layers payload = addresses.encodeVarint(len(peerList)) diff --git a/src/network/connectionchooser.py b/src/network/connectionchooser.py index 1e26b994..0770bfa6 100644 --- a/src/network/connectionchooser.py +++ b/src/network/connectionchooser.py @@ -9,14 +9,13 @@ import state def chooseConnection(stream): if state.trustedPeer: return state.trustedPeer - else: + try: + retval = portCheckerQueue.get(False) + portCheckerQueue.task_done() + except Queue.Empty: try: - retval = portCheckerQueue.get(False) - portCheckerQueue.task_done() + retval = peerDiscoveryQueue.get(False) + peerDiscoveryQueue.task_done() except Queue.Empty: - try: - retval = peerDiscoveryQueue.get(False) - peerDiscoveryQueue.task_done() - except Queue.Empty: - return random.choice(knownnodes.knownNodes[stream].keys()) - return retval + return random.choice(knownnodes.knownNodes[stream].keys()) + return retval diff --git a/src/network/connectionpool.py b/src/network/connectionpool.py index 41fb97be..67778b46 100644 --- a/src/network/connectionpool.py +++ b/src/network/connectionpool.py @@ -15,7 +15,6 @@ from network.connectionchooser import chooseConnection import network.asyncore_pollchoose as asyncore import protocol from singleton import Singleton -import shared import state @Singleton diff --git a/src/network/downloadthread.py b/src/network/downloadthread.py index e8bd44a7..a8d3e1f7 100644 --- a/src/network/downloadthread.py +++ b/src/network/downloadthread.py @@ -1,4 +1,3 @@ -import Queue import threading import addresses @@ -30,7 +29,7 @@ class DownloadThread(threading.Thread, StoppableThread): continue # keys with True values in the dict request = list((k for k, v in i.objectsNewToMe.iteritems() if v)) - if len(request) == 0: + if not request: continue if len(request) > DownloadThread.requestChunk - downloadPending: request = request[:DownloadThread.requestChunk - downloadPending] @@ -43,6 +42,3 @@ class DownloadThread(threading.Thread, StoppableThread): logger.debug("%s:%i Requesting %i objects", i.destination.host, i.destination.port, len(request)) requested += len(request) self.stop.wait(1) - - def stopThread(self): - super(DownloadThread, self).stopThread() diff --git a/src/network/invthread.py b/src/network/invthread.py index 63107a1f..9d05aec4 100644 --- a/src/network/invthread.py +++ b/src/network/invthread.py @@ -36,14 +36,14 @@ class InvThread(threading.Thread, StoppableThread): try: data = invQueue.get(False) if len(data) == 2: - BMConnectionPool().handleReceivedObject(data[0], data[1]) + BMConnectionPool().handleReceivedObject(data[0], data[1]) else: - BMConnectionPool().handleReceivedObject(data[0], data[1], data[2]) + BMConnectionPool().handleReceivedObject(data[0], data[1], data[2]) self.holdHash (data[0], data[1]) except Queue.Empty: break - if len(self.collectionOfInvs[iterator]) > 0: + if self.collectionOfInvs[iterator]: for connection in BMConnectionPool().inboundConnections.values() + BMConnectionPool().outboundConnections.values(): hashes = [] for stream in connection.streams: @@ -57,23 +57,23 @@ class InvThread(threading.Thread, StoppableThread): pass except KeyError: continue - if len(hashes) > 0: + if hashes: connection.writeQueue.put(protocol.CreatePacket('inv', addresses.encodeVarint(len(hashes)) + "".join(hashes))) self.collectionOfInvs[iterator] = {} iterator += 1 iterator %= InvThread.size self.stop.wait(1) - def holdHash(self, stream, hash): + def holdHash(self, stream, hashId): i = random.randrange(0, InvThread.size) if stream not in self.collectionOfInvs[i]: self.collectionOfInvs[i][stream] = [] - self.collectionOfInvs[i][stream].append(hash) + self.collectionOfInvs[i][stream].append(hashId) - def hasHash(self, hash): + def hasHash(self, hashId): for streamlist in self.collectionOfInvs: for stream in streamlist: - if hash in streamlist[stream]: + if hashId in streamlist[stream]: return True return False diff --git a/src/network/stats.py b/src/network/stats.py index b348098c..e17d1041 100644 --- a/src/network/stats.py +++ b/src/network/stats.py @@ -18,21 +18,19 @@ def connectedHostsList(): if BMConfigParser().get("network", "asyncore"): retval = [] for i in BMConnectionPool().inboundConnections.values() + BMConnectionPool().outboundConnections.values(): - if not i.connected: + if not i.fullyEstablished: continue try: retval.append((i.destination, i.streams[0])) except AttributeError: pass return retval - else: - return shared.connectedHostsList.items() + return shared.connectedHostsList.items() def sentBytes(): if BMConfigParser().get("network", "asyncore"): return asyncore.sentBytes - else: - return throttle.SendThrottle().total + return throttle.SendThrottle().total def uploadSpeed(): global lastSentTimestamp, lastSentBytes, currentSentSpeed @@ -44,14 +42,12 @@ def uploadSpeed(): lastSentBytes = currentSentBytes lastSentTimestamp = currentTimestamp return currentSentSpeed - else: - return throttle.sendThrottle().getSpeed() + return throttle.sendThrottle().getSpeed() def receivedBytes(): if BMConfigParser().get("network", "asyncore"): return asyncore.receivedBytes - else: - return throttle.ReceiveThrottle().total + return throttle.ReceiveThrottle().total def downloadSpeed(): global lastReceivedTimestamp, lastReceivedBytes, currentReceivedSpeed @@ -63,8 +59,7 @@ def downloadSpeed(): lastReceivedBytes = currentReceivedBytes lastReceivedTimestamp = currentTimestamp return currentReceivedSpeed - else: - return throttle.ReceiveThrottle().getSpeed() + return throttle.ReceiveThrottle().getSpeed() def pendingDownload(): if BMConfigParser().get("network", "asyncore"): @@ -73,8 +68,7 @@ def pendingDownload(): for k in connection.objectsNewToMe.keys(): tmp[k] = True return len(tmp) - else: - return PendingDownloadQueue.totalSize() + return PendingDownloadQueue.totalSize() def pendingUpload(): if BMConfigParser().get("network", "asyncore"): @@ -84,5 +78,4 @@ def pendingUpload(): for k in connection.objectsNewToThem.keys(): tmp[k] = True return len(tmp) - else: - return PendingUpload().len() + return PendingUpload().len() diff --git a/src/network/tls.py b/src/network/tls.py index 9694b4b9..5a8a5a59 100644 --- a/src/network/tls.py +++ b/src/network/tls.py @@ -63,28 +63,26 @@ class TLSDispatcher(AdvancedDispatcher): def writable(self): try: - if self.tlsStarted and not self.tlsDone and len(self.write_buf) == 0 and self.writeQueue.empty(): + if self.tlsStarted and not self.tlsDone and not self.write_buf and self.writeQueue.empty(): #print "tls writable, %r" % (self.want_write) return self.want_write - else: - return AdvancedDispatcher.writable(self) + return AdvancedDispatcher.writable(self) except AttributeError: return AdvancedDispatcher.writable(self) def readable(self): try: - if self.tlsStarted and not self.tlsDone and len(self.write_buf) == 0 and self.writeQueue.empty(): + if self.tlsStarted and not self.tlsDone and not self.write_buf and self.writeQueue.empty(): #print "tls readable, %r" % (self.want_read) return self.want_read - else: - return AdvancedDispatcher.readable(self) + return AdvancedDispatcher.readable(self) except AttributeError: return AdvancedDispatcher.readable(self) def handle_read(self): try: # wait for write buffer flush - if self.tlsStarted and not self.tlsDone and len(self.write_buf) == 0 and self.writeQueue.empty(): + if self.tlsStarted and not self.tlsDone and not self.write_buf and self.writeQueue.empty(): #print "handshaking (read)" self.tls_handshake() else: @@ -104,7 +102,7 @@ class TLSDispatcher(AdvancedDispatcher): def handle_write(self): try: # wait for write buffer flush - if self.tlsStarted and not self.tlsDone and len(self.write_buf) == 0 and self.writeQueue.empty(): + if self.tlsStarted and not self.tlsDone and not self.write_buf and self.writeQueue.empty(): #print "handshaking (write)" self.tls_handshake() else: @@ -123,13 +121,13 @@ class TLSDispatcher(AdvancedDispatcher): def tls_handshake(self): # wait for flush - if len(self.write_buf) > 0: + if self.write_buf: return False # Perform the handshake. try: #print "handshaking (internal)" self.sslSocket.do_handshake() - except ssl.SSLError, err: + except ssl.SSLError as err: #print "%s:%i: handshake fail" % (self.destination.host, self.destination.port) self.want_read = self.want_write = False if err.args[0] == ssl.SSL_ERROR_WANT_READ: diff --git a/src/network/udp.py b/src/network/udp.py index 9eccbf67..6770e5a0 100644 --- a/src/network/udp.py +++ b/src/network/udp.py @@ -1,32 +1,15 @@ -import base64 -from binascii import hexlify -import hashlib -import math import time import Queue import socket -import struct -import random -import traceback -from addresses import calculateInventoryHash from debug import logger -from inventory import Inventory -import knownnodes from network.advanceddispatcher import AdvancedDispatcher -from network.bmproto import BMProtoError, BMProtoInsufficientDataError, BMProtoExcessiveDataError, BMProto -from network.bmobject import BMObject, BMObjectInsufficientPOWError, BMObjectInvalidDataError, BMObjectExpiredError, BMObjectUnwantedStreamError, BMObjectInvalidError, BMObjectAlreadyHaveError -import network.connectionpool -from network.downloadqueue import DownloadQueue -from network.node import Node +from network.bmproto import BMProtoError, BMProtoInsufficientDataError, BMProto +from network.bmobject import BMObject, BMObjectInsufficientPOWError, BMObjectInvalidDataError, BMObjectExpiredError, BMObjectInvalidError, BMObjectAlreadyHaveError import network.asyncore_pollchoose as asyncore from network.objectracker import ObjectTracker -from network.uploadqueue import UploadQueue, UploadElem, AddrUploadQueue, ObjUploadQueue -import addresses -from bmconfigparser import BMConfigParser -from queues import objectProcessorQueue, peerDiscoveryQueue, portCheckerQueue, UISignalQueue -import shared +from queues import objectProcessorQueue, peerDiscoveryQueue, UISignalQueue import state import protocol @@ -35,7 +18,7 @@ class UDPSocket(BMProto): announceInterval = 60 def __init__(self, host=None, sock=None): - BMProto.__init__(self, sock) + super(BMProto, self).__init__(sock=sock) self.verackReceived = True self.verackSent = True # TODO sort out streams diff --git a/src/storage/sqlite.py b/src/storage/sqlite.py index 96733a36..03c80e49 100644 --- a/src/storage/sqlite.py +++ b/src/storage/sqlite.py @@ -47,7 +47,7 @@ class SqliteInventory(InventoryStorage): with self.lock: return len(self._inventory) + sqlQuery('SELECT count(*) FROM inventory')[0][0] - def by_type_and_tag(self, type, tag): + def by_type_and_tag(self, objectType, tag): with self.lock: values = [value for value in self._inventory.values() if value.type == type and value.tag == tag] values += (InventoryItem(*value) for value in sqlQuery('SELECT objecttype, streamnumber, payload, expirestime, tag FROM inventory WHERE objecttype=? AND tag=?', type, tag)) diff --git a/src/storage/storage.py b/src/storage/storage.py index 22dfbef5..302c84a0 100644 --- a/src/storage/storage.py +++ b/src/storage/storage.py @@ -30,7 +30,7 @@ class InventoryStorage(Storage, collections.MutableMapping): def __len__(self): raise NotImplementedError - def by_type_and_tag(self, type, tag): + def by_type_and_tag(self, objectType, tag): raise NotImplementedError def hashes_by_stream(self, stream): From bfbdd7e140965d374ac1f33ac9f6b45eae5b9d65 Mon Sep 17 00:00:00 2001 From: Peter Surda Date: Sat, 24 Jun 2017 12:14:23 +0200 Subject: [PATCH 110/407] Chan address validator button feedback - During validation, the button not only turns gray but also changes label --- src/bitmessageqt/addressvalidator.py | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/bitmessageqt/addressvalidator.py b/src/bitmessageqt/addressvalidator.py index 56352b72..f9de70a2 100644 --- a/src/bitmessageqt/addressvalidator.py +++ b/src/bitmessageqt/addressvalidator.py @@ -15,6 +15,8 @@ class AddressPassPhraseValidatorMixin(): self.buttonBox = buttonBox self.addressMandatory = addressMandatory self.isValid = False + # save default text + self.okButtonLabel = self.buttonBox.button(QtGui.QDialogButtonBox.Ok).text() def setError(self, string): if string is not None and self.feedBackObject is not None: @@ -26,6 +28,10 @@ class AddressPassPhraseValidatorMixin(): self.isValid = False if self.buttonBox: self.buttonBox.button(QtGui.QDialogButtonBox.Ok).setEnabled(False) + if string is not None and self.feedBackObject is not None: + self.buttonBox.button(QtGui.QDialogButtonBox.Ok).setText(_translate("AddressValidator", "Invalid")) + else: + self.buttonBox.button(QtGui.QDialogButtonBox.Ok).setText(_translate("AddressValidator", "Validating...")) def setOK(self, string): if string is not None and self.feedBackObject is not None: @@ -37,6 +43,7 @@ class AddressPassPhraseValidatorMixin(): self.isValid = True if self.buttonBox: self.buttonBox.button(QtGui.QDialogButtonBox.Ok).setEnabled(True) + self.buttonBox.button(QtGui.QDialogButtonBox.Ok).setText(self.okButtonLabel) def checkQueue(self): gotOne = False From 26eb54a82e842cbcfbbb7ce288441f7d835975c0 Mon Sep 17 00:00:00 2001 From: Peter Surda Date: Sat, 24 Jun 2017 12:16:12 +0200 Subject: [PATCH 111/407] Network status updates - now lists each node with its info instead of a per-stream summary --- src/bitmessageqt/networkstatus.py | 70 ++++++++++++++----------------- src/bitmessageqt/networkstatus.ui | 32 ++++++++++---- src/network/stats.py | 2 +- 3 files changed, 56 insertions(+), 48 deletions(-) diff --git a/src/bitmessageqt/networkstatus.py b/src/bitmessageqt/networkstatus.py index ce30b4c7..4ede68f7 100644 --- a/src/bitmessageqt/networkstatus.py +++ b/src/bitmessageqt/networkstatus.py @@ -16,6 +16,8 @@ class NetworkStatus(QtGui.QWidget, RetranslateMixin): super(NetworkStatus, self).__init__(parent) widgets.load('networkstatus.ui', self) + self.tableWidgetConnectionCount.horizontalHeader().setResizeMode(QtGui.QHeaderView.ResizeToContents) + self.startup = time.localtime() self.labelStartupTime.setText(_translate("networkstatus", "Since startup on %1").arg( l10n.formatTimestamp(self.startup))) @@ -74,51 +76,41 @@ class NetworkStatus(QtGui.QWidget, RetranslateMixin): "networkstatus", "Up: %1/s Total: %2").arg(self.formatByteRate(network.stats.uploadSpeed()), self.formatBytes(network.stats.sentBytes()))) def updateNetworkStatusTab(self): - totalNumberOfConnectionsFromAllStreams = 0 # One would think we could use len(sendDataQueues) for this but the number doesn't always match: just because we have a sendDataThread running doesn't mean that the connection has been fully established (with the exchange of version messages). - streamNumberTotals = {} connectedHosts = network.stats.connectedHostsList() - for host, streamNumber in connectedHosts: - if not streamNumber in streamNumberTotals: - streamNumberTotals[streamNumber] = 1 - else: - streamNumberTotals[streamNumber] += 1 - while self.tableWidgetConnectionCount.rowCount() > 0: - self.tableWidgetConnectionCount.removeRow(0) - for streamNumber, connectionCount in streamNumberTotals.items(): + self.tableWidgetConnectionCount.setUpdatesEnabled(False) + #self.tableWidgetConnectionCount.setSortingEnabled(False) + #self.tableWidgetConnectionCount.clearContents() + self.tableWidgetConnectionCount.setRowCount(0) + for i in connectedHosts: self.tableWidgetConnectionCount.insertRow(0) - if streamNumber == 0: - newItem = QtGui.QTableWidgetItem("?") + self.tableWidgetConnectionCount.setItem(0, 0, + QtGui.QTableWidgetItem("%s:%i" % (i.destination.host, i.destination.port)) + ) + self.tableWidgetConnectionCount.setItem(0, 1, + QtGui.QTableWidgetItem("%s" % (i.userAgent)) + ) + self.tableWidgetConnectionCount.setItem(0, 2, + QtGui.QTableWidgetItem("%s" % (i.tlsVersion)) + ) + self.tableWidgetConnectionCount.setItem(0, 3, + QtGui.QTableWidgetItem("%s" % (",".join(map(str,i.streams)))) + ) + if i.isOutbound: + brush = QtGui.QBrush(QtGui.QColor("yellow"), QtCore.Qt.SolidPattern) else: - newItem = QtGui.QTableWidgetItem(str(streamNumber)) - newItem.setFlags( - QtCore.Qt.ItemIsSelectable | QtCore.Qt.ItemIsEnabled) - self.tableWidgetConnectionCount.setItem(0, 0, newItem) - newItem = QtGui.QTableWidgetItem(str(connectionCount)) - newItem.setFlags( - QtCore.Qt.ItemIsSelectable | QtCore.Qt.ItemIsEnabled) - self.tableWidgetConnectionCount.setItem(0, 1, newItem) - """for currentRow in range(self.tableWidgetConnectionCount.rowCount()): - rowStreamNumber = int(self.tableWidgetConnectionCount.item(currentRow,0).text()) - if streamNumber == rowStreamNumber: - foundTheRowThatNeedsUpdating = True - self.tableWidgetConnectionCount.item(currentRow,1).setText(str(connectionCount)) - #totalNumberOfConnectionsFromAllStreams += connectionCount - if foundTheRowThatNeedsUpdating == False: - #Add a line to the table for this stream number and update its count with the current connection count. - self.tableWidgetConnectionCount.insertRow(0) - newItem = QtGui.QTableWidgetItem(str(streamNumber)) - newItem.setFlags( QtCore.Qt.ItemIsSelectable | QtCore.Qt.ItemIsEnabled ) - self.tableWidgetConnectionCount.setItem(0,0,newItem) - newItem = QtGui.QTableWidgetItem(str(connectionCount)) - newItem.setFlags( QtCore.Qt.ItemIsSelectable | QtCore.Qt.ItemIsEnabled ) - self.tableWidgetConnectionCount.setItem(0,1,newItem) - totalNumberOfConnectionsFromAllStreams += connectionCount""" + brush = QtGui.QBrush(QtGui.QColor("green"), QtCore.Qt.SolidPattern) + for j in (range(1)): + self.tableWidgetConnectionCount.item(0, j).setBackground(brush) + self.tableWidgetConnectionCount.setUpdatesEnabled(True) + #self.tableWidgetConnectionCount.setSortingEnabled(True) + #self.tableWidgetConnectionCount.horizontalHeader().setSortIndicator(1, QtCore.Qt.AscendingOrder) self.labelTotalConnections.setText(_translate( "networkstatus", "Total Connections: %1").arg(str(len(connectedHosts)))) - if len(connectedHosts) > 0 and shared.statusIconColor == 'red': # FYI: The 'singlelistener' thread sets the icon color to green when it receives an incoming connection, meaning that the user's firewall is configured correctly. + # FYI: The 'singlelistener' thread sets the icon color to green when it receives an incoming connection, meaning that the user's firewall is configured correctly. + if connectedHosts and shared.statusIconColor == 'red': self.window().setStatusIcon('yellow') - elif len(connectedHosts) == 0: + elif not connectedHosts and shared.statusIconColor != "red": self.window().setStatusIcon('red') # timer driven @@ -133,6 +125,6 @@ class NetworkStatus(QtGui.QWidget, RetranslateMixin): self.updateNumberOfPubkeysProcessed() def retranslateUi(self): - super(QtGui.QWidget, self).retranslateUi() + super(NetworkStatus, self).retranslateUi() self.labelStartupTime.setText(_translate("networkstatus", "Since startup on %1").arg( l10n.formatTimestamp(self.startup))) diff --git a/src/bitmessageqt/networkstatus.ui b/src/bitmessageqt/networkstatus.ui index 6ae988bf..e4264124 100644 --- a/src/bitmessageqt/networkstatus.ui +++ b/src/bitmessageqt/networkstatus.ui @@ -7,7 +7,7 @@ 0 0 602 - 252 + 254 @@ -18,12 +18,12 @@ - + 20 - QLayout::SetDefaultConstraint + QLayout::SetNoConstraint @@ -31,7 +31,7 @@ 20 - QLayout::SetFixedSize + QLayout::SetMinimumSize @@ -89,13 +89,16 @@ false - true + false QAbstractItemView::NoSelection - false + true + + + 80 false @@ -108,12 +111,22 @@ - Stream # + Peer - Connections + User agent + + + + + TLS + + + + + Stream # @@ -125,6 +138,9 @@ 4 + + QLayout::SetNoConstraint + diff --git a/src/network/stats.py b/src/network/stats.py index e17d1041..7f7c83ac 100644 --- a/src/network/stats.py +++ b/src/network/stats.py @@ -21,7 +21,7 @@ def connectedHostsList(): if not i.fullyEstablished: continue try: - retval.append((i.destination, i.streams[0])) + retval.append(i) except AttributeError: pass return retval From b9d60f8b41ea5ba28ed3bc958953c7ac27fff9dd Mon Sep 17 00:00:00 2001 From: Peter Surda Date: Sat, 24 Jun 2017 12:17:01 +0200 Subject: [PATCH 112/407] Max known nodes configurable --- src/bmconfigparser.py | 3 +++ src/knownnodes.py | 4 ++-- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/src/bmconfigparser.py b/src/bmconfigparser.py index 0db58103..6725a4d8 100644 --- a/src/bmconfigparser.py +++ b/src/bmconfigparser.py @@ -23,6 +23,9 @@ BMConfigDefaults = { "storage": "sqlite", "acceptmismatch": False, }, + "knownnodes": { + "maxnodes": 20000, + }, "zlib": { 'maxsize': 1048576 } diff --git a/src/knownnodes.py b/src/knownnodes.py index ffb14edc..8f566d43 100644 --- a/src/knownnodes.py +++ b/src/knownnodes.py @@ -1,12 +1,12 @@ import pickle import threading +from bmconfigparser import BMConfigParser import state knownNodesLock = threading.Lock() knownNodes = {} -knownNodesMax = 20000 knownNodesTrimAmount = 2000 def saveKnownNodes(dirName = None): @@ -17,7 +17,7 @@ def saveKnownNodes(dirName = None): pickle.dump(knownNodes, output) def trimKnownNodes(recAddrStream = 1): - if len(knownNodes[recAddrStream]) < knownNodesMax: + if len(knownNodes[recAddrStream]) < BMConfigParser().get("knownnodes", "maxnodes"): return with knownNodesLock: oldestList = sorted(knownNodes[recAddrStream], key=knownNodes[recAddrStream].get)[:knownNodesTrimAmount] From e9edf70d3a7775e7b8d11e9858da163b0b2d2853 Mon Sep 17 00:00:00 2001 From: Peter Surda Date: Sat, 24 Jun 2017 12:18:15 +0200 Subject: [PATCH 113/407] TLS updates - save TLS version - minor TLS error handling updates --- src/network/tls.py | 24 +++++++++++++++++++----- 1 file changed, 19 insertions(+), 5 deletions(-) diff --git a/src/network/tls.py b/src/network/tls.py index 5a8a5a59..9dafaab2 100644 --- a/src/network/tls.py +++ b/src/network/tls.py @@ -31,6 +31,7 @@ class TLSDispatcher(AdvancedDispatcher): self.ciphers = ciphers self.tlsStarted = False self.tlsDone = False + self.tlsVersion = "N/A" self.isSSL = False def state_tls_init(self): @@ -96,8 +97,9 @@ class TLSDispatcher(AdvancedDispatcher): elif err.errno in _DISCONNECTED_SSL: self.handle_close() return - else: - raise + logger.info("SSL Error: %s", str(err)) + self.handle_close() + return def handle_write(self): try: @@ -116,8 +118,9 @@ class TLSDispatcher(AdvancedDispatcher): elif err.errno in _DISCONNECTED_SSL: self.handle_close() return 0 - else: - raise + logger.info("SSL Error: %s", str(err)) + self.handle_close() + return def tls_handshake(self): # wait for flush @@ -138,8 +141,19 @@ class TLSDispatcher(AdvancedDispatcher): self.want_write = True if not (self.want_write or self.want_read): raise + except socket.error as err: + if err.errno in asyncore._DISCONNECTED: + self.handle_close() + else: + raise else: - logger.debug("%s:%i: TLS handshake success%s", self.destination.host, self.destination.port, ", TLS protocol version: %s" % (self.sslSocket.version()) if sys.version_info >= (2, 7, 9) else "") + if sys.version_info >= (2, 7, 9): + self.tlsVersion = self.sslSocket.version() + logger.debug("%s:%i: TLS handshake success, TLS protocol version: %s", + self.destination.host, self.destination.port, self.sslSocket.version()) + else: + self.tlsVersion = "TLSv1" + logger.debug("%s:%i: TLS handshake success", self.destination.host, self.destination.port) # The handshake has completed, so remove this channel and... self.del_channel() self.set_socket(self.sslSocket) From dc5a91f32630d7edfd89b5ffbd5813e6814d58a0 Mon Sep 17 00:00:00 2001 From: Peter Surda Date: Sat, 24 Jun 2017 12:19:19 +0200 Subject: [PATCH 114/407] Remove stack depth warnings - I was never able to trigger them --- src/network/advanceddispatcher.py | 11 +++++------ src/network/receivequeuethread.py | 10 +++++----- 2 files changed, 10 insertions(+), 11 deletions(-) diff --git a/src/network/advanceddispatcher.py b/src/network/advanceddispatcher.py index 61dd197b..1abc7975 100644 --- a/src/network/advanceddispatcher.py +++ b/src/network/advanceddispatcher.py @@ -41,14 +41,13 @@ class AdvancedDispatcher(asyncore.dispatcher): if not self.connected: return maxLoop = 20 - try: - sys._getframe(200) - logger.error("Stack depth warning") - except ValueError: - pass +# try: +# sys._getframe(200) +# logger.error("Stack depth warning") +# except ValueError: +# pass while maxLoop > 0: try: -# print "Trying to handle state \"%s\"" % (self.state) if getattr(self, "state_" + str(self.state))() is False: break except AttributeError: diff --git a/src/network/receivequeuethread.py b/src/network/receivequeuethread.py index 6662a078..8360ab45 100644 --- a/src/network/receivequeuethread.py +++ b/src/network/receivequeuethread.py @@ -25,11 +25,11 @@ class ReceiveQueueThread(threading.Thread, StoppableThread): while not self._stopped and state.shutdown == 0: if lastprinted < int(time.time()): lastprinted = int(time.time()) - try: - sys._getframe(200) - logger.error("Stack depth warning") - except ValueError: - pass +# try: +# sys._getframe(200) +# logger.error("Stack depth warning") +# except ValueError: +# pass processed = 0 for i in BMConnectionPool().inboundConnections.values() + BMConnectionPool().outboundConnections.values(): if self._stopped: From d57b0c55eec4ddcd22fdddb9800093e34fcadcf2 Mon Sep 17 00:00:00 2001 From: Peter Surda Date: Sat, 24 Jun 2017 12:21:06 +0200 Subject: [PATCH 115/407] Object validator trigger moved - from bmproto to bmobject --- src/network/bmobject.py | 26 +++++++++++++++----------- src/network/bmproto.py | 13 +++---------- 2 files changed, 18 insertions(+), 21 deletions(-) diff --git a/src/network/bmobject.py b/src/network/bmobject.py index 318cbfd1..98a14b5e 100644 --- a/src/network/bmobject.py +++ b/src/network/bmobject.py @@ -32,7 +32,7 @@ class BMObject(object): # min TTL, 3 hour (in the past minTTL = -3600 - def __init__(self, nonce, expiresTime, objectType, version, streamNumber, data): + def __init__(self, nonce, expiresTime, objectType, version, streamNumber, data, payloadOffset): self.nonce = nonce self.expiresTime = expiresTime self.objectType = objectType @@ -40,7 +40,7 @@ class BMObject(object): self.streamNumber = streamNumber self.inventoryHash = calculateInventoryHash(data) self.data = data - self.tag = '' + self.tag = data[payloadOffset:payloadOffset+32] def checkProofOfWorkSufficient(self): # Let us check to make sure that the proof of work is sufficient. @@ -69,6 +69,17 @@ class BMObject(object): if self.inventoryHash in Inventory(): raise BMObjectAlreadyHaveError() + def checkObjectByType(self): + if self.objectType == protocol.OBJECT_GETPUBKEY: + self.checkGetpubkey() + elif self.objectType == protocol.OBJECT_PUBKEY: + self.checkPubkey() + elif self.objectType == protocol.OBJECT_MSG: + self.checkMessage() + elif self.objectType == protocol.OBJECT_BROADCAST: + self.checkBroadcast() + # other objects don't require other types of tests + def checkMessage(self): return @@ -77,15 +88,12 @@ class BMObject(object): logger.info('getpubkey message doesn\'t contain enough data. Ignoring.') raise BMObjectInvalidError() - def checkPubkey(self, tag): + def checkPubkey(self): if len(self.data) < 146 or len(self.data) > 440: # sanity check logger.info('pubkey object too short or too long. Ignoring.') raise BMObjectInvalidError() - if self.version >= 4: - self.tag = tag - logger.debug('tag in received pubkey is: %s' % hexlify(tag)) - def checkBroadcast(self, tag): + def checkBroadcast(self): if len(self.data) < 180: logger.debug('The payload length of this broadcast packet is unreasonably low. Someone is probably trying funny business. Ignoring message.') raise BMObjectInvalidError() @@ -93,7 +101,3 @@ class BMObject(object): # this isn't supported anymore if self.version < 2: raise BMObjectInvalidError() - - if self.version >= 3: - self.tag = tag - logger.debug('tag in received broadcast is: %s' % hexlify(tag)) diff --git a/src/network/bmproto.py b/src/network/bmproto.py index 32bf038c..9b1ba08e 100644 --- a/src/network/bmproto.py +++ b/src/network/bmproto.py @@ -263,7 +263,7 @@ class BMProto(AdvancedDispatcher, ObjectTracker): def bm_command_object(self): objectOffset = self.payloadOffset nonce, expiresTime, objectType, version, streamNumber = self.decode_payload_content("QQIvv") - self.object = BMObject(nonce, expiresTime, objectType, version, streamNumber, self.payload) + self.object = BMObject(nonce, expiresTime, objectType, version, streamNumber, self.payload, self.payloadOffset) if len(self.payload) - self.payloadOffset > BMProto.maxObjectPayloadSize: logger.info('The payload length of this object is too large (%s bytes). Ignoring it.' % len(self.payload) - self.payloadOffset) @@ -291,15 +291,8 @@ class BMProto(AdvancedDispatcher, ObjectTracker): isinstance(e, BMObjectExpiredError): raise e - if self.object.objectType == protocol.OBJECT_GETPUBKEY: - self.object.checkGetpubkey() - elif self.object.objectType == protocol.OBJECT_PUBKEY: - self.object.checkPubkey(self.payload[self.payloadOffset:self.payloadOffset+32]) - elif self.object.objectType == protocol.OBJECT_MSG: - self.object.checkMessage() - elif self.object.objectType == protocol.OBJECT_BROADCAST: - self.object.checkBroadcast(self.payload[self.payloadOffset:self.payloadOffset+32]) - # other objects don't require other types of tests + self.object.checkObjectByType() + Inventory()[self.object.inventoryHash] = ( self.object.objectType, self.object.streamNumber, self.payload[objectOffset:], self.object.expiresTime, self.object.tag) objectProcessorQueue.put((self.object.objectType,self.object.data)) From 0a79490e2c8f500fa7f6e23c811ca4ef29606030 Mon Sep 17 00:00:00 2001 From: Peter Surda Date: Sat, 24 Jun 2017 12:21:42 +0200 Subject: [PATCH 116/407] Known nodes maximum configurable part 2 --- src/network/bmproto.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/network/bmproto.py b/src/network/bmproto.py index 9b1ba08e..61cc53d3 100644 --- a/src/network/bmproto.py +++ b/src/network/bmproto.py @@ -317,7 +317,7 @@ class BMProto(AdvancedDispatcher, ObjectTracker): peer = state.Peer(decodedIP, port) if peer in knownnodes.knownNodes[stream] and knownnodes.knownNodes[stream][peer] > seenTime: continue - if len(knownnodes.knownNodes[stream]) < 20000: + if len(knownnodes.knownNodes[stream]) < BMConfigParser().get("knownnodes", "maxnodes"): with knownnodes.knownNodesLock: knownnodes.knownNodes[stream][peer] = seenTime #knownnodes.knownNodes[stream][peer] = seenTime From 0dc0b22974f3767105e0f2dfb5eb2e9d8d840414 Mon Sep 17 00:00:00 2001 From: Peter Surda Date: Sat, 24 Jun 2017 12:22:41 +0200 Subject: [PATCH 117/407] Expired / Stream mismatch / duplicate object error handling - cleanup of the code --- src/network/bmproto.py | 28 ++++++++++++++++++++++------ 1 file changed, 22 insertions(+), 6 deletions(-) diff --git a/src/network/bmproto.py b/src/network/bmproto.py index 61cc53d3..0c70f12e 100644 --- a/src/network/bmproto.py +++ b/src/network/bmproto.py @@ -7,6 +7,7 @@ import socket import struct from addresses import calculateInventoryHash +from bmconfigparser import BMConfigParser from debug import logger from inventory import Inventory import knownnodes @@ -272,10 +273,10 @@ class BMProto(AdvancedDispatcher, ObjectTracker): self.object.checkProofOfWorkSufficient() try: self.object.checkEOLSanity() - self.object.checkStream() self.object.checkAlreadyHave() - except (BMObjectExpiredError, BMObjectUnwantedStreamError, BMObjectAlreadyHaveError) as e: - for connection in network.connectionpool.BMConnectionPool().inboundConnections.values() + network.connectionpool.BMConnectionPool().outboundConnections.values(): + except (BMObjectExpiredError, BMObjectAlreadyHaveError) as e: + for connection in network.connectionpool.BMConnectionPool().inboundConnections.values() + \ + network.connectionpool.BMConnectionPool().outboundConnections.values(): try: with connection.objectsNewToThemLock: del connection.objectsNewToThem[self.object.inventoryHash] @@ -286,9 +287,24 @@ class BMProto(AdvancedDispatcher, ObjectTracker): del connection.objectsNewToMe[self.object.inventoryHash] except KeyError: pass - if not BMConfigParser().get("inventory", "acceptmismatch") or \ - isinstance(e, BMObjectAlreadyHaveError) or \ - isinstance(e, BMObjectExpiredError): + raise e + try: + self.object.checkStream() + except (BMObjectUnwantedStreamError,) as e: + for connection in network.connectionpool.BMConnectionPool().inboundConnections.values() + \ + network.connectionpool.BMConnectionPool().outboundConnections.values(): + try: + with connection.objectsNewToMeLock: + del connection.objectsNewToMe[self.object.inventoryHash] + except KeyError: + pass + if not BMConfigParser().get("inventory", "acceptmismatch"): + try: + with connection.objectsNewToThemLock: + del connection.objectsNewToThem[self.object.inventoryHash] + except KeyError: + pass + if not BMConfigParser().get("inventory", "acceptmismatch"): raise e self.object.checkObjectByType() From 916b85c862d740b4efe8fb3f73fa4ae0489b81b1 Mon Sep 17 00:00:00 2001 From: Peter Surda Date: Sat, 24 Jun 2017 12:23:16 +0200 Subject: [PATCH 118/407] Connection pool cleanup - minor code quality improvement --- src/network/connectionpool.py | 40 ++++++++++++++++++----------------- 1 file changed, 21 insertions(+), 19 deletions(-) diff --git a/src/network/connectionpool.py b/src/network/connectionpool.py index 67778b46..ae76c318 100644 --- a/src/network/connectionpool.py +++ b/src/network/connectionpool.py @@ -69,7 +69,7 @@ class BMConnectionPool(object): def removeConnection(self, connection): if isinstance(connection, network.udp.UDPSocket): del self.udpSockets[connection.listening.host] - if isinstance(connection, network.tcp.TCPServer): + elif isinstance(connection, network.tcp.TCPServer): del self.listeningSockets[state.Peer(connection.destination.host, connection.destination.port)] elif connection.isOutbound: try: @@ -163,24 +163,26 @@ class BMConnectionPool(object): self.lastSpawned = time.time() - if acceptConnections and len(self.listeningSockets) == 0: - self.startListening() - logger.info('Listening for incoming connections.') - if acceptConnections and len(self.udpSockets) == 0: - if BMConfigParser().safeGet("network", "bind") is None: - self.startUDPSocket() - else: - for bind in re.sub("[^\w.]+", " ", BMConfigParser().safeGet("network", "bind")).split(): - self.startUDPSocket(bind) - logger.info('Starting UDP socket(s).') - if len(self.listeningSockets) > 0 and not acceptConnections: - for i in self.listeningSockets: - i.handle_close() - logger.info('Stopped listening for incoming connections.') - if len(self.udpSockets) > 0 and not acceptConnections: - for i in self.udpSockets: - i.handle_close() - logger.info('Stopped udp sockets.') + if acceptConnections: + if not self.listeningSockets: + self.startListening() + logger.info('Listening for incoming connections.') + if not self.udpSockets: + if BMConfigParser().safeGet("network", "bind") is None: + self.startUDPSocket() + else: + for bind in re.sub("[^\w.]+", " ", BMConfigParser().safeGet("network", "bind")).split(): + self.startUDPSocket(bind) + logger.info('Starting UDP socket(s).') + else: + if self.listeningSockets: + for i in self.listeningSockets: + i.handle_close() + logger.info('Stopped listening for incoming connections.') + if self.udpSockets: + for i in self.udpSockets: + i.handle_close() + logger.info('Stopped udp sockets.') # while len(asyncore.socket_map) > 0 and state.shutdown == 0: # print "loop, state = %s" % (proxy.state) From 189578cba389423a54c6541d60353a8c209bd62e Mon Sep 17 00:00:00 2001 From: Peter Surda Date: Sat, 24 Jun 2017 12:23:56 +0200 Subject: [PATCH 119/407] Asyncore proxy fixes - SOCKS5 now seems to work, SOCKS4a untested --- src/network/proxy.py | 41 +++++++++++++++++++++++++------- src/network/socks4a.py | 21 +++++++++-------- src/network/socks5.py | 53 ++++++++++++++++++++++++------------------ src/network/tcp.py | 12 +++++++--- 4 files changed, 83 insertions(+), 44 deletions(-) diff --git a/src/network/proxy.py b/src/network/proxy.py index 3b7cc848..c131c22a 100644 --- a/src/network/proxy.py +++ b/src/network/proxy.py @@ -1,13 +1,36 @@ import socket - -import state +import time from advanceddispatcher import AdvancedDispatcher import asyncore_pollchoose as asyncore +from debug import logger import network.connectionpool +import state + +class ProxyError(Exception): + errorCodes = ("UnknownError") + + def __init__(self, code): + self.code = code + try: + self.message = self.__class__.errorCodes[self.code] + except IndexError: + self.message = self.__class__.errorCodes[-1] + super(ProxyError, self).__init__(self.message) + + +class GeneralProxyError(ProxyError): + errorCodes = ("Success", + "Invalid data", + "Not connected", + "Not available", + "Bad proxy type", + "Bad input", + "Timed out", + "Network unreachable", + "Connection refused", + "Host unreachable") -class ProxyError(Exception): pass -class GeneralProxyError(ProxyError): pass class Proxy(AdvancedDispatcher): # these are global, and if you change config during runtime, all active/new @@ -22,7 +45,8 @@ class Proxy(AdvancedDispatcher): @proxy.setter def proxy(self, address): - if type(address) != tuple or (len(address) < 2) or (type(str(address[0])) != type('')) or (type(address[1]) != int): + if not isinstance(address, tuple) or (len(address) < 2) or \ + (not isinstance(address[0], str) or not isinstance(address[1], int)): raise ValueError self.__class__._proxy = address @@ -42,18 +66,17 @@ class Proxy(AdvancedDispatcher): self.isOutbound = True self.create_socket(socket.AF_INET, socket.SOCK_STREAM) self.connect(self.proxy) - print "connecting in background to %s:%i" % (self.proxy[0], self.proxy[1]) def handle_connect(self): + self.set_state("init") try: AdvancedDispatcher.handle_connect(self) except socket.error as e: if e.errno in asyncore._DISCONNECTED: - logger.debug("%s:%i: Connection failed: %s" % (self.destination.host, self.destination.port, str(e))) + logger.debug("%s:%i: Connection failed: %s", self.destination.host, self.destination.port, str(e)) return + self.state_init() def state_proxy_handshake_done(self): - self.writeQueue.put(protocol.assembleVersionMessage(self.destination.host, self.destination.port, network.connectionpool.BMConnectionPool().streams, False)) self.connectedAt = time.time() return False - diff --git a/src/network/socks4a.py b/src/network/socks4a.py index 08bd18cf..61f3ec7d 100644 --- a/src/network/socks4a.py +++ b/src/network/socks4a.py @@ -1,11 +1,14 @@ import socket import struct -from advanceddispatcher import AdvancedDispatcher -import asyncore_pollchoose as asyncore from proxy import Proxy, ProxyError, GeneralProxyError -class Socks4aError(ProxyError): pass +class Socks4aError(ProxyError): + errorCodes = ("Request granted", + "Request rejected or failed", + "Request rejected because SOCKS server cannot connect to identd on the client", + "Request rejected because the client program and identd report different user-ids", + "Unknown error") class Socks4a(Proxy): @@ -24,22 +27,20 @@ class Socks4a(Proxy): if self.read_buf[0:1] != chr(0x00).encode(): # bad data self.close() - raise Socks4aError + raise GeneralProxyError(1) elif self.read_buf[1:2] != chr(0x5A).encode(): # Connection failed self.close() if ord(self.read_buf[1:2]) in (91, 92, 93): - # socks 4 erro - raise Socks4aError - #raise Socks5Error((ord(resp[1:2]), _socks5errors[ord(resp[1:2])-90])) + # socks 4 error + raise Socks4aError(ord(self.read_buf[1:2]) - 90) else: - raise Socks4aError - #raise Socks4aError((94, _socks4aerrors[4])) + raise Socks4aError(4) # Get the bound address/port self.boundport = struct.unpack(">H", self.read_buf[2:4])[0] self.boundaddr = self.read_buf[4:] self.__proxysockname = (self.boundaddr, self.boundport) - if self.ipaddr != None: + if self.ipaddr: self.__proxypeername = (socket.inet_ntoa(self.ipaddr), self.destination[1]) else: self.__proxypeername = (self.destination[0], self.destport) diff --git a/src/network/socks5.py b/src/network/socks5.py index 6745308c..89c3c9b2 100644 --- a/src/network/socks5.py +++ b/src/network/socks5.py @@ -1,13 +1,27 @@ import socket import struct -from advanceddispatcher import AdvancedDispatcher -import asyncore_pollchoose as asyncore from proxy import Proxy, ProxyError, GeneralProxyError -import network.connectionpool -class Socks5AuthError(ProxyError): pass -class Socks5Error(ProxyError): pass +class Socks5AuthError(ProxyError): + errorCodes = ("Succeeded", + "Authentication is required", + "All offered authentication methods were rejected", + "Unknown username or invalid password", + "Unknown error") + + +class Socks5Error(ProxyError): + errorCodes = ("Succeeded", + "General SOCKS server failure", + "Connection not allowed by ruleset", + "Network unreachable", + "Host unreachable", + "Connection refused", + "TTL expired", + "Command not supported", + "Address type not supported", + "Unknown error") class Socks5(Proxy): @@ -30,7 +44,7 @@ class Socks5(Proxy): self.read_buf = self.read_buf[2:] if ret[0] != 5: # general error - raise GeneralProxyError + raise GeneralProxyError(1) elif ret[1] == 0: # no auth required self.set_state("auth_done", 2) @@ -39,14 +53,14 @@ class Socks5(Proxy): self.writeQueue.put(struct.pack('BB', 1, len(self._auth[0])) + \ self._auth[0] + struct.pack('B', len(self._auth[1])) + \ self._auth[1]) - self.set_state("auth_1", 2) + self.set_state("auth_needed", 2) else: if ret[1] == 0xff: # auth error - raise Socks5AuthError + raise Socks5AuthError(2) else: # other error - raise Socks5Error + raise GeneralProxyError(1) def state_auth_needed(self): if not self.read_buf_sufficient(2): @@ -54,10 +68,10 @@ class Socks5(Proxy): ret = struct.unpack('BB', self.read_buf) if ret[0] != 1: # general error - raise Socks5Error + raise GeneralProxyError(1) if ret[1] != 0: # auth error - raise Socks5AuthError + raise Socks5AuthError(3) # all ok self.set_state = ("auth_done", 2) @@ -66,19 +80,15 @@ class Socks5(Proxy): return False # Get the response if self.read_buf[0:1] != chr(0x05).encode(): - # general error self.close() - raise Socks5Error + raise GeneralProxyError(1) elif self.read_buf[1:2] != chr(0x00).encode(): # Connection failed self.close() if ord(self.read_buf[1:2])<=8: - # socks 5 erro - raise Socks5Error - #raise Socks5Error((ord(resp[1:2]), _socks5errors[ord(resp[1:2])])) + raise Socks5Error(ord(self.read_buf[1:2])) else: - raise Socks5Error - #raise Socks5Error((9, _socks5errors[9])) + raise Socks5Error(9) # Get the bound address/port elif self.read_buf[3:4] == chr(0x01).encode(): self.set_state("proxy_addr_1", 4) @@ -86,8 +96,7 @@ class Socks5(Proxy): self.set_state("proxy_addr_2_1", 4) else: self.close() - #raise GeneralProxyError((1,_generalerrors[1])) - raise GeneralProxyError + raise GeneralProxyError(1) def state_proxy_addr_1(self): if not self.read_buf_sufficient(4): @@ -112,14 +121,14 @@ class Socks5(Proxy): return False self.boundport = struct.unpack(">H", self.read_buf[0:2])[0] self.__proxysockname = (self.boundaddr, self.boundport) - if self.ipaddr != None: + if self.ipaddr is not None: self.__proxypeername = (socket.inet_ntoa(self.ipaddr), self.destination[1]) else: self.__proxypeername = (self.destination[0], self.destport) self.set_state("proxy_handshake_done", 2) def proxy_sock_name(self): - return socket.inet_ntoa(self.__proxysockname[0]) + return socket.inet_ntoa(self.__proxysockname[0]) class Socks5Connection(Socks5): diff --git a/src/network/tcp.py b/src/network/tcp.py index 8a6b5705..bd925063 100644 --- a/src/network/tcp.py +++ b/src/network/tcp.py @@ -181,7 +181,10 @@ class Socks5BMConnection(Socks5Connection, TCPConnection): TCPConnection.__init__(self, address=address, sock=self.socket) self.set_state("init") - def state_socks_handshake_done(self): + def state_proxy_handshake_done(self): + Socks5Connection.state_proxy_handshake_done(self) + self.writeQueue.put(protocol.assembleVersionMessage(self.destination.host, self.destination.port, \ + network.connectionpool.BMConnectionPool().streams, False)) self.set_state("bm_header", expectBytes=protocol.Header.size) return False @@ -192,7 +195,10 @@ class Socks4aBMConnection(Socks4aConnection, TCPConnection): TCPConnection.__init__(self, address=address, sock=self.socket) self.set_state("init") - def state_socks_handshake_done(self): + def state_proxy_handshake_done(self): + Socks4aConnection.state_proxy_handshake_done(self) + self.writeQueue.put(protocol.assembleVersionMessage(self.destination.host, self.destination.port, \ + network.connectionpool.BMConnectionPool().streams, False)) self.set_state("bm_header", expectBytes=protocol.Header.size) return False @@ -216,7 +222,7 @@ class TCPServer(AdvancedDispatcher): len(network.connectionpool.BMConnectionPool().outboundConnections) > \ BMConfigParser().safeGetInt("bitmessagesettings", "maxtotalconnections") + \ BMConfigParser().safeGetInt("bitmessagesettings", "maxbootstrapconnections"): - close(sock) + sock.close() return try: network.connectionpool.BMConnectionPool().addConnection(TCPConnection(sock=sock)) From aa203b23eec8ff9ccbde3678f01f4ee14f43a09f Mon Sep 17 00:00:00 2001 From: Peter Surda Date: Sat, 24 Jun 2017 23:09:08 +0200 Subject: [PATCH 120/407] Fix typo introduced by code quality patch --- src/storage/sqlite.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/storage/sqlite.py b/src/storage/sqlite.py index 03c80e49..b5c48e3b 100644 --- a/src/storage/sqlite.py +++ b/src/storage/sqlite.py @@ -49,8 +49,8 @@ class SqliteInventory(InventoryStorage): def by_type_and_tag(self, objectType, tag): with self.lock: - values = [value for value in self._inventory.values() if value.type == type and value.tag == tag] - values += (InventoryItem(*value) for value in sqlQuery('SELECT objecttype, streamnumber, payload, expirestime, tag FROM inventory WHERE objecttype=? AND tag=?', type, tag)) + values = [value for value in self._inventory.values() if value.type == objectType and value.tag == tag] + values += (InventoryItem(*value) for value in sqlQuery('SELECT objecttype, streamnumber, payload, expirestime, tag FROM inventory WHERE objecttype=? AND tag=?', objectType, tag)) return values def hashes_by_stream(self, stream): From ccfe58c2c0e54043fb9a6f6e85a724e400dad261 Mon Sep 17 00:00:00 2001 From: PyBitmessage Translations Date: Sun, 25 Jun 2017 16:49:31 +0200 Subject: [PATCH 121/407] Auto-updated language ja from transifex --- src/translations/bitmessage_ja.qm | Bin 65447 -> 65563 bytes src/translations/bitmessage_ja.ts | 295 ++++++++++++++++-------------- 2 files changed, 155 insertions(+), 140 deletions(-) diff --git a/src/translations/bitmessage_ja.qm b/src/translations/bitmessage_ja.qm index da6c715b15c0b1ffea8a707e3fcbaa5d83dafa2c..d9acc50c19b7d0bde1dd7fc8242ce639528ca365 100644 GIT binary patch delta 1172 zcmX|Bdr(wW9RBXzyLb2AyUPxb;brWyB<|n{EKDpZ@<>F`VL?J5N-B$0e1H$YNhyJ0 zXetERIWRdy^Z+BAP#eYsZ6zU)N6QpMGr*99JfslDh$afOW6k)-Z@!uHednCt?>pa| z#z(Rbia4*Wt(oq9i?!8u{Zs9)eqtNn2B?()f`GnStCx>!x-)R|4dA7XK*1d#Ydi3} z*=qLT4|f1Jwp+6|c+Po4-uf;up9;Q7M)`awQe!9%SetdsdaHc{I~i+TPGho&nQ_yX zBkka6U}_03T#W#H<#;i73*FsRVI1O676oF zfHcbu%mNvf>x4-@`2wA%FN>_nb&mD?J11$6QT}}Gu4&-G`^29&`Ak2}kEhau?%v`T z_#7bbCxugGA&~lyB2*jzzFr}vyiPxSC#_P_BlD zPNg%zn-!|Eu1f0Tys9eFlLn}Ol!}qmhAz<{bg0G(hG^uc$+CEW=_eVIfhi-UQh?aM z$wBF!K(l$6*yv&tnU;5F z7+bcfJ6l^!-6^ayXZ+Fl>EjOpJS}dw#d3li@AGIW?@`KJc{M}!{{{q49gt-6! delta 1101 zcmXAneNa?Y7{;G__wL=j_b%*!Bgh(ifutRni4_WPKo-gnY=R9PAWHg}K!r(&5N7~~ zk0G1V66j_B$i!sANE#H|NH8`k1s7@=2d57}1VRuMWVD!3Q|UDQ^~{`k&+|U#cb-pc zgtiq;;KuH}HA72B9G>9E`d)8wO*{)2?gEGehBh<;sK^AIk;FZoIISFzx)j?a#pT@0(sTvcTP14Kt>W5Z0!L?|DrTrKudS=e2j*nz(E4-!)UUO02WU~NN>$>v<1F=%K&wU7CsBNVFH4g2qfU=H0Jv7H zI~08dh)vKn_#mQghuP(9kIz+qeRB1WBh`>_F#K7dlUx!tr7W$Vb-hfy(mFaxf05iR4G{Ts%6z2 z$f5m`E$WvUCH*YBvbRymSYiM5=O4v&H$5Id)^Kc@#)HXKeQ{Di_;n))1qca~81XI%v3a py>qMAxo#SF`3VKZMWr<9_IIux$mE?(Zdv0T{Mp1gANSmi`WNy^bISk# diff --git a/src/translations/bitmessage_ja.ts b/src/translations/bitmessage_ja.ts index 1757b887..65a5c57d 100644 --- a/src/translations/bitmessage_ja.ts +++ b/src/translations/bitmessage_ja.ts @@ -58,12 +58,12 @@ EmailGatewayRegistrationDialog - + Registration failed: 登録に失敗しました: - + The requested email address is not available, please try a new one. Fill out the new desired email address (including @mailchuck.com) below: リクエストしたメールアドレスは利用できません。新しいメールアドレスをお試しください。 新しい希望メールアドレス (@mailchuck.com を含む) を次のように記入してください: @@ -318,7 +318,7 @@ Please type the desired email address (including @mailchuck.com) below: メッセージの確認を受け取りました %1 - + Broadcast queued. 配信がキューに入りました。 @@ -348,7 +348,7 @@ Please type the desired email address (including @mailchuck.com) below: 不明なステータス: %1 %2 - + Not Connected 未接続 @@ -507,22 +507,22 @@ It is important that you back up this file. Would you like to open the file now? - + Connection lost 接続が切断されました - + Connected 接続済み - + Message trashed メッセージが削除されました - + The TTL, or Time-To-Live is the length of time that the network will hold the message. The recipient must get it during this time. If your Bitmessage client does not hear an acknowledgement, it will resend the message automatically. The longer the Time-To-Live, the @@ -533,17 +533,17 @@ It is important that you back up this file. Would you like to open the file now? コンピュータがメッセージを送信するために必要な処理が増えます。 多くの場合 4〜5 日のTTL(Time-To-Live)が適切です。 - + Message too long メッセージが長すぎます - + The message that you are trying to send is too long by %1 bytes. (The maximum is 261644 bytes). Please cut it down before sending. 送信しようとしているメッセージが %1 バイト長すぎます。 (最大は261644バイトです)。 送信する前に短くしてください。 - + Error: Your account wasn't registered at an email gateway. Sending registration now as %1, please wait for the registration to be processed before retrying sending. エラー: アカウントがメールゲートウェイに登録されていません。 今 %1 として登録を送信しています。送信を再試行する前に、登録が処理されるまでお待ちください。 @@ -588,67 +588,67 @@ It is important that you back up this file. Would you like to open the file now? - + Error: You must specify a From address. If you don't have one, go to the 'Your Identities' tab. エラー: 送信元アドレスを指定してください。まだ作成していない場合には「アドレス一覧」のタブを開いてください。 - + Address version number アドレスのバージョン番号 - + Concerning the address %1, Bitmessage cannot understand address version numbers of %2. Perhaps upgrade Bitmessage to the latest version. アドレス %1 に接続。%2 のバージョン番号は処理できません。Bitmessageを最新のバージョンへアップデートしてください。 - + Stream number ストリーム番号 - + Concerning the address %1, Bitmessage cannot handle stream numbers of %2. Perhaps upgrade Bitmessage to the latest version. アドレス %1 に接続。%2 のストリーム番号は処理できません。Bitmessageを最新のバージョンへアップデートしてください。 - + Warning: You are currently not connected. Bitmessage will do the work necessary to send the message but it won't send until you connect. 警告: 接続されていません。Bitmessageはメッセージの処理を行いますが、ネットワークに接続するまで送信はされません。 - + Message queued. メッセージがキューに入りました。 - + Your 'To' field is empty. 宛先が指定されていません。 - + Right click one or more entries in your address book and select 'Send message to this address'. アドレス帳から一つ、または複数のアドレスを右クリックして「このアドレスへ送信」を選んでください。 - + Fetched address from namecoin identity. namecoin IDからアドレスを取得。 - + New Message 新規メッセージ - + From 送信元 - + Sending email gateway registration request メールゲートウェイの登録リクエストを送信しています @@ -663,142 +663,142 @@ It is important that you back up this file. Would you like to open the file now? 入力されたアドレスは不正です。無視されました。 - + Error: You cannot add the same address to your address book twice. Try renaming the existing one if you want. エラー: 同じアドレスを複数アドレス帳に追加する事はできません。既存の項目をリネームしてください。 - + Error: You cannot add the same address to your subscriptions twice. Perhaps rename the existing one if you want. エラー: 購読に、同じアドレスを2回追加することはできません。 必要に応じて、既存の名前を変更してください。 - + Restart 再開 - + You must restart Bitmessage for the port number change to take effect. ポート番号の変更を有効にするにはBitmessageを再起動してください。 - + Bitmessage will use your proxy from now on but you may want to manually restart Bitmessage now to close existing connections (if any). プロキシの設定を有効にするには手動でBitmessageを再起動してください。既に接続がある場合は切断されます。 - + Number needed 数字が必要です - + Your maximum download and upload rate must be numbers. Ignoring what you typed. 最大ダウンロード数とアップロード数は数字にする必要があります。 入力されたものを無視します。 - + Will not resend ever 今後再送信されません - + Note that the time limit you entered is less than the amount of time Bitmessage waits for the first resend attempt therefore your messages will never be resent. 入力した時間制限は、Bitmessageが最初の再送試行を待つ時間よりも短いため、メッセージは再送信されないことにご注意ください。 - + Sending email gateway unregistration request メールゲートウェイの登録抹消リクエストを送信しています - + Sending email gateway status request メールゲートウェイの状態リクエストを送信しています - + Passphrase mismatch パスフレーズが一致しません - + The passphrase you entered twice doesn't match. Try again. 再度入力されたパスフレーズが一致しません。再入力してください。 - + Choose a passphrase パスフレーズを選択してください - + You really do need a passphrase. パスフレーズが必要です。 - + Address is gone アドレスが無効になりました - + Bitmessage cannot find your address %1. Perhaps you removed it? アドレス %1 が見つかりません。既に削除していませんか? - + Address disabled アドレスが無効になりました - + Error: The address from which you are trying to send is disabled. You'll have to enable it on the 'Your Identities' tab before using it. エラー: 送信しようとしたアドレスは無効になっています。使用する前に「アドレス一覧」で有効にしてください。 - + Entry added to the Address Book. Edit the label to your liking. アドレス帳に項目が追加されました。ラベルは自由に編集できます。 - + Entry added to the blacklist. Edit the label to your liking. ブラックリストに項目が追加されました。ラベルは自由に編集できます。 - + Error: You cannot add the same address to your blacklist twice. Try renaming the existing one if you want. エラー: ブラックリストに同じアドレスを2回追加することはできません。 必要に応じて既存の名前を変更してみてください。 - + Moved items to trash. アイテムをゴミ箱へ移動。 - + Undeleted item. アイテムの削除を元に戻します。 - + Save As... 形式を選択して保存 - + Write error. 書き込みエラー。 - + No addresses selected. アドレスが未選択です。 - + If you delete the subscription, messages that you already received will become inaccessible. Maybe you can consider disabling the subscription instead. Disabled subscriptions will not receive new messages, but you can still view messages you already received. Are you sure you want to delete the subscription? @@ -807,7 +807,7 @@ Are you sure you want to delete the subscription? 購読を削除してもよろしいですか? - + If you delete the channel, messages that you already received will become inaccessible. Maybe you can consider disabling the channel instead. Disabled channels will not receive new messages, but you can still view messages you already received. Are you sure you want to delete the channel? @@ -816,92 +816,92 @@ Are you sure you want to delete the channel? チャンネルを削除してもよろしいですか? - + Do you really want to remove this avatar? このアバターを削除してもよろしいですか? - + You have already set an avatar for this address. Do you really want to overwrite it? すでにこのアドレスのアバターを設定しています。 上書きしてもよろしいですか? - + Start-on-login not yet supported on your OS. ログイン時に開始は、まだお使いのOSでサポートされていません。 - + Minimize-to-tray not yet supported on your OS. トレイに最小化は、まだお使いのOSでサポートされていません。 - + Tray notifications not yet supported on your OS. トレイ通知は、まだお使いのOSでサポートされていません。 - + Testing... テスト中 - + This is a chan address. You cannot use it as a pseudo-mailing list. chanアドレスは仮想メーリングリストのアドレスには使用できません。 - + The address should start with ''BM-'' アドレスは「BM-」から始まります - + The address is not typed or copied correctly (the checksum failed). このアドレスは正しく入力、またはコピーされていません。(チェックサムが一致しません)。 - + The version number of this address is higher than this software can support. Please upgrade Bitmessage. このアドレスのバージョン番号はこのプログラムのサポート範囲外です。Bitmessageをアップデートしてください。 - + The address contains invalid characters. 入力されたアドレスは不正な文字を含んでいます。 - + Some data encoded in the address is too short. このアドレスでエンコードされたデータが短すぎます。 - + Some data encoded in the address is too long. このアドレスでエンコードされたデータが長過ぎます。 - + Some data encoded in the address is malformed. このアドレスでエンコードされた一部のデータが不正です。 - + Enter an address above. 上にアドレスを入力してください。 - + Address is an old type. We cannot display its past broadcasts. アドレスが古い形式です。 過去の配信は表示できません。 - + There are no recent broadcasts from this address to display. このアドレスから表示する最近の配信はありません。 - + You are using TCP port %1. (This can be changed in the settings). 使用中のポート %1 (設定で変更できます)。 @@ -1111,47 +1111,47 @@ Are you sure you want to delete the channel? 新しい項目を追加 - + Display the %1 recent broadcast(s) from this address. このアドレスから%1の最新の配信を表示します。 - + New version of PyBitmessage is available: %1. Download it from https://github.com/Bitmessage/PyBitmessage/releases/latest 新しいバージョンの PyBitmessage が利用可能です: %1。 https://github.com/Bitmessage/PyBitmessage/releases/latest からダウンロードしてください - + Waiting for PoW to finish... %1% PoW(証明)が完了するのを待っています... %1% - + Shutting down Pybitmessage... %1% Pybitmessageをシャットダウンしています... %1% - + Waiting for objects to be sent... %1% オブジェクトの送信待ち... %1% - + Saving settings... %1% 設定を保存しています... %1% - + Shutting down core... %1% コアをシャットダウンしています... %1% - + Stopping notifications... %1% 通知を停止しています... %1% - + Shutdown imminent... %1% すぐにシャットダウンします... %1% @@ -1166,7 +1166,7 @@ Are you sure you want to delete the channel? %n 日 - + Shutting down PyBitmessage... %1% PyBitmessageをシャットダウンしています... %1% @@ -1216,61 +1216,61 @@ Are you sure you want to delete the channel? アラート: ディスクまたはデータストレージのボリュームがいっぱいです。 Bitmessageが終了します。 - + Error! Could not find sender address (your address) in the keys.dat file. エラー! keys.datファイルで送信元アドレス (あなたのアドレス) を見つけることができませんでした。 - + Doing work necessary to send broadcast... 配信に必要な処理を行っています... - + Broadcast sent on %1 配信が送信されました %1 - + Encryption key was requested earlier. 暗号鍵は以前にリクエストされました。 - + Sending a request for the recipient's encryption key. 受信者の暗号鍵のリクエストを送信します。 - + Looking up the receiver's public key 受信者の公開鍵を探しています - + Problem: Destination is a mobile device who requests that the destination be included in the message but this is disallowed in your settings. %1 問題: メッセージに含まれた宛先のリクエストはモバイルデバイスですが、設定では許可されていません。 %1 - + Doing work necessary to send message. There is no required difficulty for version 2 addresses like this. メッセージの送信に必要な処理を行っています。 このようなバージョン2のアドレスには、必要な難易度はありません。 - + Doing work necessary to send message. Receiver's required difficulty: %1 and %2 メッセージの送信に必要な処理を行っています。 受信者の必要な難易度: %1 および %2 - + Problem: The work demanded by the recipient (%1 and %2) is more difficult than you are willing to do. %3 問題: 受信者が要求している処理 (%1 および %2) は、現在あなたが設定しているよりも高い難易度です。 %3 - + Problem: You are trying to send a message to yourself or a chan but your encryption key could not be found in the keys.dat file. Could not encrypt message. %1 問題: あなた自身またはチャンネルにメッセージを送信しようとしていますが、暗号鍵がkeys.datファイルに見つかりませんでした。 メッセージを暗号化できませんでした。 %1 @@ -1280,7 +1280,7 @@ Receiver's required difficulty: %1 and %2 メッセージの送信に必要な処理を行っています。 - + Message sent. Waiting for acknowledgement. Sent on %1 メッセージを送信しました。 確認応答を待っています。 %1 で送信しました @@ -1290,12 +1290,12 @@ Receiver's required difficulty: %1 and %2 暗号鍵のリクエストに必要な処理を行っています。 - + Broadcasting the public key request. This program will auto-retry if they are offline. 公開鍵のリクエストを配信しています。 このプログラムがオフラインの場合、自動的に再試行されます。 - + Sending public key request. Waiting for reply. Requested at %1 公開鍵のリクエストを送信しています。 返信を待っています。 %1 でリクエストしました @@ -1315,7 +1315,7 @@ Receiver's required difficulty: %1 and %2 すべてのメッセージを既読にする - + Are you sure you would like to mark all messages read? すべてのメッセージを既読にしてもよろしいですか? @@ -1325,37 +1325,37 @@ Receiver's required difficulty: %1 and %2 配信に必要な処理を行っています。 - + Proof of work pending PoW(証明)を待っています - + %n object(s) pending proof of work %n オブジェクトが証明待ち (PoW) - + %n object(s) waiting to be distributed %n オブジェクトが配布待ち - + Wait until these tasks finish? これらのタスクが完了するまで待ちますか? - + Problem communicating with proxy: %1. Please check your network settings. プロキシとの通信に問題があります: %1。 ネットワーク設定を確認してください。 - + SOCKS5 Authentication problem: %1. Please check your SOCKS5 settings. SOCKS5認証に問題があります: %1。 SOCKS5の設定を確認してください。 - + The time on your computer, %1, may be wrong. Please verify your settings. お使いのコンピュータの時間 %1 は間違っている可能性があります。 設定を確認してください。 @@ -1425,77 +1425,77 @@ Receiver's required difficulty: %1 and %2 チャンネルにはお勧めしません - + Problems connecting? Try enabling UPnP in the Network Settings 接続に問題がありますか? ネットワーク設定でUPnPを有効にしてみてください - + Error: Bitmessage addresses start with BM- Please check the recipient address %1 エラー: BitmessageのアドレスはBM-で始まります。 受信者のアドレス %1 を確認してください - + Error: The recipient address %1 is not typed or copied correctly. Please check it. エラー: 受信者のアドレス %1 は正しく入力、またはコピーされていません。確認して下さい。 - + Error: The recipient address %1 contains invalid characters. Please check it. エラー: 受信者のアドレス %1 は不正な文字を含んでいます。確認して下さい。 - + Error: The version of the recipient address %1 is too high. Either you need to upgrade your Bitmessage software or your acquaintance is being clever. エラー: 受信者アドレスのバージョン %1 は高すぎます。 Bitmessageソフトウェアをアップグレードする必要があるか、連絡先が賢明になっているかのいずれかです。 - + Error: Some data encoded in the recipient address %1 is too short. There might be something wrong with the software of your acquaintance. エラー: アドレス %1 でエンコードされたデータが短すぎます。連絡先のソフトウェアが何かしら誤っている可能性があります。 - + Error: Some data encoded in the recipient address %1 is too long. There might be something wrong with the software of your acquaintance. エラー: 受信者のアドレス %1 でエンコードされたデータが短すぎます。連絡先のソフトウェアが何かしら誤っている可能性があります。 - + Error: Some data encoded in the recipient address %1 is malformed. There might be something wrong with the software of your acquaintance. エラー: 受信者のアドレス %1 でエンコードされたデータの一部が不正です。連絡先のソフトウェアが何かしら誤っている可能性があります。 - + Error: Something is wrong with the recipient address %1. エラー: 受信者のアドレス %1 には何かしら誤りがあります。 - + Synchronisation pending 同期を保留しています - + Bitmessage hasn't synchronised with the network, %n object(s) to be downloaded. If you quit now, it may cause delivery delays. Wait until the synchronisation finishes? Bitmessageはネットワークと同期していません。%n のオブジェクトをダウンロードする必要があります。 今、終了すると、配送が遅れることがあります。 同期が完了するまで待ちますか? - + Not connected 未接続 - + Bitmessage isn't connected to the network. If you quit now, it may cause delivery delays. Wait until connected and the synchronisation finishes? Bitmessageはネットワークに接続していません。 今、終了すると、配送が遅れることがあります。 接続して、同期が完了するまで待ちますか? - + Waiting for network connection... ネットワーク接続を待っています... - + Waiting for finishing synchronisation... 同期の完了を待っています... @@ -1526,14 +1526,14 @@ Receiver's required difficulty: %1 and %2 MsgDecode - + The message has an unknown encoding. Perhaps you should upgrade Bitmessage. メッセージのエンコードが不明です。 Bitmessageをアップグレードする必要があるかもしれません。 - + Unknown encoding 不明なエンコード @@ -1841,77 +1841,77 @@ The 'Random Number' option is selected by default but deterministic ad 接続数: - + Since startup: 起動日時: - + Processed 0 person-to-person messages. 0 通の1対1のメッセージを処理しました。 - + Processed 0 public keys. 0 件の公開鍵を処理しました。 - + Processed 0 broadcasts. 0 件の配信を処理しました。 - + Inventory lookups per second: 0 毎秒のインベントリ検索: 0 - + Objects to be synced: 同期する必要のあるオブジェクト: - + Stream # ストリーム # Connections - 接続 + - + Since startup on %1 起動日時 %1 - + Down: %1/s Total: %2 ダウン: %1/秒 合計: %2 - + Up: %1/s Total: %2 アップ: %1/秒 合計: %2 - + Total Connections: %1 接続数: %1 - + Inventory lookups per second: %1 毎秒のインベントリ検索: %1 - + Up: 0 kB/s アップ: 0 kB/秒 - + Down: 0 kB/s ダウン: 0 kB/秒 @@ -1921,30 +1921,45 @@ The 'Random Number' option is selected by default but deterministic ad ネットワークの状態 - + byte(s) バイト - + Object(s) to be synced: %n 同期する必要のあるオブジェクト: %n - + Processed %n person-to-person message(s). %n 通の1対1のメッセージを処理しました。 - + Processed %n broadcast message(s). %n 件の配信を処理しました。 - + Processed %n public key(s). %n 件の公開鍵を処理しました。 + + + Peer + ピア + + + + User agent + ユーザーエージェント + + + + TLS + TLS + newChanDialog From 30b65aaefc3652afee007556908e98807bfaed77 Mon Sep 17 00:00:00 2001 From: PyBitmessage Translations Date: Sat, 24 Jun 2017 21:24:59 +0200 Subject: [PATCH 122/407] Auto-updated language pl from transifex --- src/translations/bitmessage_pl.qm | Bin 89786 -> 89830 bytes src/translations/bitmessage_pl.ts | 333 ++++++++++++++++-------------- 2 files changed, 174 insertions(+), 159 deletions(-) diff --git a/src/translations/bitmessage_pl.qm b/src/translations/bitmessage_pl.qm index c02118e4fa3b0d749d8214ba77f336a17e84f342..ad4818c65e659d34fa86c17fb019d922afa09297 100644 GIT binary patch delta 3804 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 diff --git a/src/translations/bitmessage_pl.ts b/src/translations/bitmessage_pl.ts index 4b2b6371..93f72222 100644 --- a/src/translations/bitmessage_pl.ts +++ b/src/translations/bitmessage_pl.ts @@ -58,12 +58,12 @@ EmailGatewayRegistrationDialog - + Registration failed: Rejestracja nie powiodła się: - + The requested email address is not available, please try a new one. Fill out the new desired email address (including @mailchuck.com) below: Wybrany adres e-mail nie jest dostępny, proszę spróbować inny. Wpisz adres poniżej (razem z końcówką @mailchuck.com): @@ -207,7 +207,7 @@ Wprowadź wybrany adres e-mail (razem z końcówką @mailchuck.com) poniżej: Save message as... - Zapisz wiadomość jako... + Zapisz wiadomość jako… @@ -232,7 +232,7 @@ Wprowadź wybrany adres e-mail (razem z końcówką @mailchuck.com) poniżej: Set avatar... - Ustaw awatar... + Ustaw awatar… @@ -242,7 +242,7 @@ Wprowadź wybrany adres e-mail (razem z końcówką @mailchuck.com) poniżej: Special address behavior... - Specjalne zachowanie adresu... + Specjalne zachowanie adresu… @@ -320,7 +320,7 @@ Wprowadź wybrany adres e-mail (razem z końcówką @mailchuck.com) poniżej:Otrzymano potwierdzenie odbioru wiadomości %1 - + Broadcast queued. Przekaz w kolejce do wysłania. @@ -350,7 +350,7 @@ Wprowadź wybrany adres e-mail (razem z końcówką @mailchuck.com) poniżej:Nieznany status: %1 %2 - + Not Connected Brak połączenia @@ -513,22 +513,22 @@ Zaleca się zrobienie kopii zapasowej tego pliku. Czy chcesz otworzyć ten plik - + Connection lost Połączenie utracone - + Connected Połączono - + Message trashed Wiadomość usunięta - + The TTL, or Time-To-Live is the length of time that the network will hold the message. The recipient must get it during this time. If your Bitmessage client does not hear an acknowledgement, it will resend the message automatically. The longer the Time-To-Live, the @@ -539,17 +539,17 @@ Im dłuższy TTL, tym więcej pracy będzie musiał wykonac komputer wysyłając Zwykle 4-5 dniowy TTL jest odpowiedni. - + Message too long Wiadomość zbyt długa - + The message that you are trying to send is too long by %1 bytes. (The maximum is 261644 bytes). Please cut it down before sending. Wiadomość jest za długa o %1 bajtów (maksymalna długość wynosi 261644 bajty). Przed wysłaniem należy ją skrócić. - + Error: Your account wasn't registered at an email gateway. Sending registration now as %1, please wait for the registration to be processed before retrying sending. Błąd: Twoje konto nie było zarejestrowane w bramce poczty. Rejestrowanie jako %1, proszę poczekać na zakończenie procesu przed ponowną próbą wysłania wiadomości. @@ -594,67 +594,67 @@ Zwykle 4-5 dniowy TTL jest odpowiedni. - + Error: You must specify a From address. If you don't have one, go to the 'Your Identities' tab. Błąd: musisz wybrać adres wysyłania. Jeżeli go nie posiadasz, przejdź do zakładki 'Twoje tożsamości'. - + Address version number Numer wersji adresu - + Concerning the address %1, Bitmessage cannot understand address version numbers of %2. Perhaps upgrade Bitmessage to the latest version. Odnośnie adresu %1, Bitmessage nie potrafi odczytać wersji adresu %2. Może uaktualnij Bitmessage do najnowszej wersji. - + Stream number Numer strumienia - + Concerning the address %1, Bitmessage cannot handle stream numbers of %2. Perhaps upgrade Bitmessage to the latest version. Odnośnie adresu %1, Bitmessage nie potrafi operować na strumieniu adresu %2. Może uaktualnij Bitmessage do najnowszej wersji. - + Warning: You are currently not connected. Bitmessage will do the work necessary to send the message but it won't send until you connect. Uwaga: nie jesteś obecnie połączony. Bitmessage wykona niezbędną pracę do wysłania wiadomości, ale nie wyśle jej póki się nie połączysz. - + Message queued. W kolejce do wysłania - + Your 'To' field is empty. Pole 'Do' jest puste - + Right click one or more entries in your address book and select 'Send message to this address'. Użyj prawego przycisku myszy na adresie z książki adresowej i wybierz opcję "Wyślij wiadomość do tego adresu". - + Fetched address from namecoin identity. Pobrano adres z identyfikatora Namecoin. - + New Message Nowa wiadomość - + From Od - + Sending email gateway registration request Wysyłanie zapytania o rejestrację na bramce poczty @@ -669,142 +669,142 @@ Zwykle 4-5 dniowy TTL jest odpowiedni. Wprowadzono niewłaściwy adres, który został zignorowany. - + Error: You cannot add the same address to your address book twice. Try renaming the existing one if you want. Błąd: Adres znajduje się już w książce adresowej. - + Error: You cannot add the same address to your subscriptions twice. Perhaps rename the existing one if you want. Błąd: Adres znajduje się już na liście subskrybcji. - + Restart Uruchom ponownie - + You must restart Bitmessage for the port number change to take effect. Musisz zrestartować Bitmessage, aby zmiana numeru portu weszła w życie. - + Bitmessage will use your proxy from now on but you may want to manually restart Bitmessage now to close existing connections (if any). Bitmessage będzie of teraz korzystał z serwera proxy, ale możesz ręcznie zrestartować Bitmessage, aby zamknąć obecne połączenia (jeżeli występują). - + Number needed Wymagany numer - + Your maximum download and upload rate must be numbers. Ignoring what you typed. Maksymalne prędkości wysyłania i pobierania powinny być liczbami. Zignorowano zmiany. - + Will not resend ever Nigdy nie wysyłaj ponownie - + Note that the time limit you entered is less than the amount of time Bitmessage waits for the first resend attempt therefore your messages will never be resent. Zauważ, że wpisany limit czasu wynosi mniej niż czas, który Bitmessage czeka przed pierwszą ponowną próbą wysłania wiadomości, więc Twoje wiadomości nie zostaną nigdy wysłane ponownie. - + Sending email gateway unregistration request Wysyłanie zapytania o wyrejestrowanie z bramki poczty - + Sending email gateway status request Wysyłanie zapytania o stan bramki poczty - + Passphrase mismatch Hasła różnią się - + The passphrase you entered twice doesn't match. Try again. Hasła, które wpisałeś nie pasują. Spróbuj ponownie. - + Choose a passphrase Wpisz hasło - + You really do need a passphrase. Naprawdę musisz wpisać hasło. - + Address is gone Adres zniknął - + Bitmessage cannot find your address %1. Perhaps you removed it? Bitmessage nie może odnaleźć Twojego adresu %1. Może go usunąłeś? - + Address disabled Adres nieaktywny - + Error: The address from which you are trying to send is disabled. You'll have to enable it on the 'Your Identities' tab before using it. Błąd: adres, z którego próbowałeś wysłać wiadomość jest nieaktywny. Włącz go w zakładce 'Twoje tożsamości' zanim go użyjesz. - + Entry added to the Address Book. Edit the label to your liking. Dodano wpis do książki adresowej. Można teraz zmienić jego nazwę. - + Entry added to the blacklist. Edit the label to your liking. Dodano wpis do listy blokowanych. Można teraz zmienić jego nazwę. - + Error: You cannot add the same address to your blacklist twice. Try renaming the existing one if you want. Błąd: Adres znajduje się już na liście blokowanych. - + Moved items to trash. Przeniesiono wiadomości do kosza. - + Undeleted item. Przywróć wiadomość. - + Save As... - Zapisz jako... + Zapisz jako… - + Write error. Błąd zapisu. - + No addresses selected. Nie wybrano adresu. - + If you delete the subscription, messages that you already received will become inaccessible. Maybe you can consider disabling the subscription instead. Disabled subscriptions will not receive new messages, but you can still view messages you already received. Are you sure you want to delete the subscription? @@ -813,7 +813,7 @@ Are you sure you want to delete the subscription? Czy na pewno chcesz usunąć tę subskrypcję? - + If you delete the channel, messages that you already received will become inaccessible. Maybe you can consider disabling the channel instead. Disabled channels will not receive new messages, but you can still view messages you already received. Are you sure you want to delete the channel? @@ -822,92 +822,92 @@ Are you sure you want to delete the channel? Czy na pewno chcesz usunąć ten kanał? - + Do you really want to remove this avatar? Czy na pewno chcesz usunąć ten awatar? - + You have already set an avatar for this address. Do you really want to overwrite it? Już ustawiłeś awatar dla tego adresu. Czy na pewno chcesz go nadpisać? - + Start-on-login not yet supported on your OS. Start po zalogowaniu jeszcze nie jest wspierany pod Twoim systemem. - + Minimize-to-tray not yet supported on your OS. Minimalizacja do zasobnika nie jest jeszcze wspierana pod Twoim systemem. - + Tray notifications not yet supported on your OS. Powiadomienia w zasobniku nie są jeszcze wspierane pod Twoim systemem. - + Testing... - Testowanie... + Testowanie… - + This is a chan address. You cannot use it as a pseudo-mailing list. To jest adres kanału. Nie możesz go użyć jako pseudo-listy-dyskusyjnej. - + The address should start with ''BM-'' Adres powinien zaczynać sie od "BM-" - + The address is not typed or copied correctly (the checksum failed). Adres nie został skopiowany lub przepisany poprawnie (błąd sumy kontrolnej). - + The version number of this address is higher than this software can support. Please upgrade Bitmessage. Numer wersji tego adresu jest wyższy niż ten program może obsłużyć. Proszę zaktualizować Bitmessage. - + The address contains invalid characters. Adres zawiera nieprawidłowe znaki. - + Some data encoded in the address is too short. Niektóre dane zakodowane w adresie są za krótkie. - + Some data encoded in the address is too long. Niektóre dane zakodowane w adresie są za długie. - + Some data encoded in the address is malformed. Niektóre dane zakodowane w adresie są uszkodzone. - + Enter an address above. Wprowadź adres powyżej. - + Address is an old type. We cannot display its past broadcasts. Adres starego typu - + There are no recent broadcasts from this address to display. Brak niedawnych wiadomości przekazów do wyświetlenia. - + You are using TCP port %1. (This can be changed in the settings). Btimessage używa portu TCP %1. (Można go zmienić w ustawieniach). @@ -1117,49 +1117,49 @@ Czy na pewno chcesz usunąć ten kanał? Dodaj nowy wpis - + Display the %1 recent broadcast(s) from this address. Pokaż %1 ostatnich wiadomości przekazów z tego adresu. - + New version of PyBitmessage is available: %1. Download it from https://github.com/Bitmessage/PyBitmessage/releases/latest Nowa wersja Bitmessage jest dostępna: %1. Pobierz ją z https://github.com/Bitmessage/PyBitmessage/releases/latest - + Waiting for PoW to finish... %1% - Oczekiwanie na wykonanie dowodu pracy... %1% + Oczekiwanie na wykonanie dowodu pracy… %1% - + Shutting down Pybitmessage... %1% - Zamykanie PyBitmessage... %1% + Zamykanie PyBitmessage… %1% - + Waiting for objects to be sent... %1% - Oczekiwanie na wysłanie obiektów... %1% + Oczekiwanie na wysłanie obiektów… %1% - + Saving settings... %1% - Zapisywanie ustawień... %1% + Zapisywanie ustawień… %1% - + Shutting down core... %1% - Zamykanie rdzenia programu... %1% - - - - Stopping notifications... %1% - Zatrzymywanie powiadomień... %1% + Zamykanie rdzenia programu… %1% + Stopping notifications... %1% + Zatrzymywanie powiadomień… %1% + + + Shutdown imminent... %1% - Zaraz zamknę... %1% + Zaraz zamknę… %1% @@ -1172,9 +1172,9 @@ Czy na pewno chcesz usunąć ten kanał? %n dzień%n dni%n dni%n dni - + Shutting down PyBitmessage... %1% - Zamykanie PyBitmessage... %1% + Zamykanie PyBitmessage… %1% @@ -1189,7 +1189,7 @@ Czy na pewno chcesz usunąć ten kanał? Done generating address. Doing work necessary to broadcast it... - Adresy wygenerowany. Wykonywanie dowodu pracy niezbędnego na jego rozesłanie... + Adresy wygenerowany. Wykonywanie dowodu pracy niezbędnego na jego rozesłanie… @@ -1222,61 +1222,61 @@ Czy na pewno chcesz usunąć ten kanał? Uwaga: Twój dysk lub partycja jest pełny. Bitmessage zamknie się. - + Error! Could not find sender address (your address) in the keys.dat file. Błąd! Nie można odnaleźć adresu nadawcy (Twojego adresu) w pliku keys.dat. - + Doing work necessary to send broadcast... - Wykonywanie dowodu pracy niezbędnego do wysłania przekazu... + Wykonywanie dowodu pracy niezbędnego do wysłania przekazu… - + Broadcast sent on %1 Przekaz wysłane o %1 - + Encryption key was requested earlier. Prośba o klucz szyfrujący została już wysłana. - + Sending a request for the recipient's encryption key. Wysyłanie zapytania o klucz szyfrujący odbiorcy. - + Looking up the receiver's public key Wyszukiwanie klucza publicznego odbiorcy - + Problem: Destination is a mobile device who requests that the destination be included in the message but this is disallowed in your settings. %1 Problem: adres docelowy jest urządzeniem przenośnym, które wymaga, aby adres docelowy był zawarty w wiadomości, ale jest to zabronione w Twoich ustawieniach. %1 - + Doing work necessary to send message. There is no required difficulty for version 2 addresses like this. Wykonywanie dowodu pracy niezbędnego do wysłania wiadomości. Nie ma wymaganej trudności dla adresów w wersji 2, takich jak ten adres. - + Doing work necessary to send message. Receiver's required difficulty: %1 and %2 Wykonywanie dowodu pracy niezbędnego do wysłania wiadomości. Odbiorca wymaga trudności: %1 i %2 - + Problem: The work demanded by the recipient (%1 and %2) is more difficult than you are willing to do. %3 Problem: dowód pracy wymagany przez odbiorcę (%1 i %2) jest trudniejszy niż chciałbyś wykonać. %3 - + Problem: You are trying to send a message to yourself or a chan but your encryption key could not be found in the keys.dat file. Could not encrypt message. %1 Problem: próbujesz wysłać wiadomość do siebie lub na kanał, ale Twój klucz szyfrujący nie został znaleziony w pliku keys.dat. Nie można zaszyfrować wiadomości. %1 @@ -1286,7 +1286,7 @@ Odbiorca wymaga trudności: %1 i %2 Wykonywanie pracy potrzebnej do wysłania wiadomości. - + Message sent. Waiting for acknowledgement. Sent on %1 Wiadomość wysłana. Oczekiwanie na potwierdzenie odbioru. Wysłano o %1 @@ -1296,12 +1296,12 @@ Odbiorca wymaga trudności: %1 i %2 Wykonywanie pracy niezbędnej do prośby o klucz szyfrujący. - + Broadcasting the public key request. This program will auto-retry if they are offline. Rozsyłanie prośby o klucz publiczny. Program spróbuje ponownie, jeżeli jest on niepołączony. - + Sending public key request. Waiting for reply. Requested at %1 Wysyłanie prośby o klucz publiczny. Oczekiwanie na odpowiedź. Zapytano o %1 @@ -1321,7 +1321,7 @@ Odbiorca wymaga trudności: %1 i %2 Oznacz wszystkie jako przeczytane - + Are you sure you would like to mark all messages read? Czy na pewno chcesz oznaczyć wszystkie wiadomości jako przeczytane? @@ -1331,37 +1331,37 @@ Odbiorca wymaga trudności: %1 i %2 Wykonywanie dowodu pracy niezbędnego do wysłania przekazu. - + Proof of work pending Dowód pracy zawieszony - + %n object(s) pending proof of work Zawieszony dowód pracy %n obiektuZawieszony dowód pracy %n obiektówZawieszony dowód pracy %n obiektówZawieszony dowód pracy %n obiektów - + %n object(s) waiting to be distributed %n obiekt oczekuje na wysłanie%n obiektów oczekuje na wysłanie%n obiektów oczekuje na wysłanie%n obiektów oczekuje na wysłanie - + Wait until these tasks finish? Czy poczekać aż te zadania zostaną zakończone? - + Problem communicating with proxy: %1. Please check your network settings. Błąd podczas komunikacji z proxy: %1. Proszę sprawdź swoje ustawienia sieci. - + SOCKS5 Authentication problem: %1. Please check your SOCKS5 settings. Problem z autoryzacją SOCKS5: %1. Proszę sprawdź swoje ustawienia SOCKS5. - + The time on your computer, %1, may be wrong. Please verify your settings. Czas na Twoim komputerze, %1, może być błędny. Proszę sprawdź swoje ustawienia. @@ -1430,79 +1430,79 @@ Witamy w przyjaznym i bezpiecznym Bitmessage niezalecany dla kanałów - + Problems connecting? Try enabling UPnP in the Network Settings Problem z połączeniem? Spróbuj włączyć UPnP w ustawieniach sieci. - + Error: Bitmessage addresses start with BM- Please check the recipient address %1 Błąd: adresy Bitmessage zaczynają się od BM-. Proszę sprawdzić adres odbiorcy %1. - + Error: The recipient address %1 is not typed or copied correctly. Please check it. Błąd: adres odbiorcy %1 nie został skopiowany lub przepisany poprawnie. Proszę go sprawdzić. - + Error: The recipient address %1 contains invalid characters. Please check it. Błąd: adres odbiorcy %1 zawiera nieprawidłowe znaki. Proszę go sprawdzić. - + Error: The version of the recipient address %1 is too high. Either you need to upgrade your Bitmessage software or your acquaintance is being clever. Błąd: wersja adresu odbiorcy %1 jest za wysoka. Musisz albo zaktualizować Twoje oprogramowanie Bitmessage, albo twój znajomy Cię trolluje. - + Error: Some data encoded in the recipient address %1 is too short. There might be something wrong with the software of your acquaintance. Błąd: niektóre dane zakodowane w adresie odbiorcy %1 są zbyt krótkie. Być może coś nie działa należycie w programie Twojego znajomego. - + Error: Some data encoded in the recipient address %1 is too long. There might be something wrong with the software of your acquaintance. Błąd: niektóre dane zakodowane w adresie odbiorcy %1 są zbyt długie. Być może coś nie działa należycie w programie Twojego znajomego. - + Error: Some data encoded in the recipient address %1 is malformed. There might be something wrong with the software of your acquaintance. Błąd: niektóre dane zakodowane w adresie odbiorcy %1 są uszkodzone. Być może coś nie działa należycie w programie Twojego znajomego. - + Error: Something is wrong with the recipient address %1. Błąd: coś jest nie tak z adresem odbiorcy %1. - + Synchronisation pending Synchronizacja zawieszona - + Bitmessage hasn't synchronised with the network, %n object(s) to be downloaded. If you quit now, it may cause delivery delays. Wait until the synchronisation finishes? Bitmessage nie zsynchronizował się z siecią, %n obiekt oczekuje na pobranie. Jeżeli zamkniesz go teraz, może to spowodować opóźnienia dostarczeń. Czy poczekać na zakończenie synchronizacji?Bitmessage nie zsynchronizował się z siecią, %n obiekty oczekują na pobranie. Jeżeli zamkniesz go teraz, może to spowodować opóźnienia dostarczeń. Czy poczekać na zakończenie synchronizacji?Bitmessage nie zsynchronizował się z siecią, %n obiektów oczekuje na pobranie. Jeżeli zamkniesz go teraz, może to spowodować opóźnienia dostarczeń. Czy poczekać na zakończenie synchronizacji?Bitmessage nie zsynchronizował się z siecią, %n obiektów oczekuje na pobranie. Jeżeli zamkniesz go teraz, może to spowodować opóźnienia dostarczeń. Czy poczekać na zakończenie synchronizacji? - + Not connected Niepołączony - + Bitmessage isn't connected to the network. If you quit now, it may cause delivery delays. Wait until connected and the synchronisation finishes? Bitmessage nie połączył się z siecią. Jeżeli zamkniesz go teraz, może to spowodować opóźnienia dostarczeń. Czy poczekać na połączenie i zakończenie synchronizacji? - + Waiting for network connection... - Oczekiwanie na połączenie sieciowe... + Oczekiwanie na połączenie sieciowe… - + Waiting for finishing synchronisation... - Oczekiwanie na zakończenie synchronizacji... + Oczekiwanie na zakończenie synchronizacji… @@ -1531,14 +1531,14 @@ Witamy w przyjaznym i bezpiecznym Bitmessage MsgDecode - + The message has an unknown encoding. Perhaps you should upgrade Bitmessage. Wiadomość zawiera nierozpoznane kodowanie. Prawdopodobnie powinieneś zaktualizować Bitmessage. - + Unknown encoding Nierozpoznane kodowanie @@ -1847,77 +1847,77 @@ Generowanie adresów 'losowych' jest wybrane domyślnie, jednak determ Wszystkich połączeń: - + Since startup: Od startu: - + Processed 0 person-to-person messages. Przetworzono 0 wiadomości zwykłych. - + Processed 0 public keys. Przetworzono 0 kluczy publicznych. - + Processed 0 broadcasts. Przetworzono 0 wiadomości przekazów. - + Inventory lookups per second: 0 Zapytań o elementy na sekundę: 0 - + Objects to be synced: Obiektów do zsynchronizowania: - + Stream # Strumień # Connections - Połączeń + - + Since startup on %1 Od startu programu o %1 - + Down: %1/s Total: %2 Pobieranie: %1/s W całości: %2 - + Up: %1/s Total: %2 Wysyłanie: %1/s W całości: %2 - + Total Connections: %1 Wszystkich połączeń: %1 - + Inventory lookups per second: %1 Zapytań o elementy na sekundę: %1 - + Up: 0 kB/s Wysyłanie: 0 kB/s - + Down: 0 kB/s Pobieranie: 0 kB/s @@ -1927,30 +1927,45 @@ Generowanie adresów 'losowych' jest wybrane domyślnie, jednak determ Stan sieci - + byte(s) bajtbajtówbajtówbajtów - + Object(s) to be synced: %n Jeden obiekt do zsynchronizowaniaObieków do zsynchronizowania: %nObieków do zsynchronizowania: %nObieków do zsynchronizowania: %n - + Processed %n person-to-person message(s). Przetworzono %n wiadomość zwykłą.Przetworzono %n wiadomości zwykłych.Przetworzono %n wiadomości zwykłych.Przetworzono %n wiadomości zwykłych. - + Processed %n broadcast message(s). Przetworzono %n wiadomość przekazów.Przetworzono %n wiadomości przekazów.Przetworzono %n wiadomości przekazów.Przetworzono %n wiadomości przekazów. - + Processed %n public key(s). Przetworzono %n klucz publiczny.Przetworzono %n kluczy publicznych.Przetworzono %n kluczy publicznych.Przetworzono %n kluczy publicznych. + + + Peer + Użytkownik + + + + User agent + Klient + + + + TLS + TLS + newChanDialog From 8a3577aed74be6ccbe31871fbff7915a28a38a07 Mon Sep 17 00:00:00 2001 From: PyBitmessage Translations Date: Sat, 24 Jun 2017 21:23:27 +0200 Subject: [PATCH 123/407] Auto-updated language eo from transifex --- src/translations/bitmessage_eo.qm | Bin 86543 -> 86609 bytes src/translations/bitmessage_eo.ts | 335 ++++++++++++++++-------------- 2 files changed, 175 insertions(+), 160 deletions(-) diff --git a/src/translations/bitmessage_eo.qm b/src/translations/bitmessage_eo.qm index eddaf80806073c740d5119e830181d97b2877ddc..073654d4927ecb567df87b758b63d8c5f3be1f5a 100644 GIT binary patch delta 3824 zcmYjUdsvNG7ys?^zVA8bJy%`GFq%j%sU(#y%B>JNE|JS9g+5(4Q7+ZsM6QX_De9

|bOTKmLdFJ{4IM46d``vr3wb%Ns-`*#)Sg0)) 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 EmailGatewayRegistrationDialog - + Registration failed: Registrado malsukcesis: - + The requested email address is not available, please try a new one. Fill out the new desired email address (including @mailchuck.com) below: La dezirata retpoŝtadreso ne estas disponebla, bonvolu provi alian. Entajpu novan deziratan adreson (kune kun @mailchuck.com) sube: @@ -203,7 +203,7 @@ Bonvolu entajpi deziratan retpoŝtadreson (kune kun @mailchuck.com) sube: Save message as... - Konservi mesaĝon kiel... + Konservi mesaĝon kiel… @@ -228,7 +228,7 @@ Bonvolu entajpi deziratan retpoŝtadreson (kune kun @mailchuck.com) sube: Set avatar... - Agordi avataron... + Agordi avataron… @@ -238,7 +238,7 @@ Bonvolu entajpi deziratan retpoŝtadreson (kune kun @mailchuck.com) sube: Special address behavior... - Speciala sinteno de adreso... + Speciala sinteno de adreso… @@ -316,7 +316,7 @@ Bonvolu entajpi deziratan retpoŝtadreson (kune kun @mailchuck.com) sube:Ricevis konfirmon de la mesaĝo je %1 - + Broadcast queued. Elsendo en atendovico. @@ -346,7 +346,7 @@ Bonvolu entajpi deziratan retpoŝtadreson (kune kun @mailchuck.com) sube:Nekonata stato: %1 %2 - + Not Connected Ne konektita @@ -509,22 +509,22 @@ Estas grava, ke vi faru sekurkopion de tiu dosiero. Ĉu vi volas malfermi la dos - + Connection lost Perdis konekton - + Connected Konektita - + Message trashed Movis mesaĝon al rubujo - + The TTL, or Time-To-Live is the length of time that the network will hold the message. The recipient must get it during this time. If your Bitmessage client does not hear an acknowledgement, it will resend the message automatically. The longer the Time-To-Live, the @@ -532,17 +532,17 @@ Estas grava, ke vi faru sekurkopion de tiu dosiero. Ĉu vi volas malfermi la dos La vivdaŭro signifas ĝis kiam la reto tenos la mesaĝon. La ricevonto devos elŝuti ĝin dum tiu tempo. Se via bitmesaĝa kliento ne ricevos konfirmon, ĝi resendos mesaĝon aŭtomate. Ju pli longa vivdaŭro, des pli laboron via komputilo bezonos fari por sendi mesaĝon. Vivdaŭro proksimume kvin aŭ kvar horoj estas ofte konvena. - + Message too long Mesaĝo tro longa - + The message that you are trying to send is too long by %1 bytes. (The maximum is 261644 bytes). Please cut it down before sending. La mesaĝon kiun vi provis sendi estas tro longa je %1 bitokoj. (La maksimumo estas 261644 bitokoj.) Bonvolu mallongigi ĝin antaŭ sendado. - + Error: Your account wasn't registered at an email gateway. Sending registration now as %1, please wait for the registration to be processed before retrying sending. Eraro: Via konto ne estas registrita je retpoŝta kluzo. Registranta nun kiel %1, bonvolu atendi ĝis la registrado finos antaŭ vi reprovos sendi iun ajn. @@ -587,67 +587,67 @@ Estas grava, ke vi faru sekurkopion de tiu dosiero. Ĉu vi volas malfermi la dos - + Error: You must specify a From address. If you don't have one, go to the 'Your Identities' tab. Eraro: Vi devas elekti sendontan adreson. Se vi ne havas iun, iru al langeto 'Viaj identigoj'. - + Address version number Numero de adresversio - + Concerning the address %1, Bitmessage cannot understand address version numbers of %2. Perhaps upgrade Bitmessage to the latest version. Dum prilaborado de adreso adreso %1, Bitmesaĝo ne povas kompreni numerojn %2 de adresversioj. Eble ĝisdatigu Bitmesaĝon al la plej nova versio. - + Stream number Fluo numero - + Concerning the address %1, Bitmessage cannot handle stream numbers of %2. Perhaps upgrade Bitmessage to the latest version. Dum prilaborado de adreso %1, Bitmesaĝo ne povas priservi %2 fluojn numerojn. Eble ĝisdatigu Bitmesaĝon al la plej nova versio. - + Warning: You are currently not connected. Bitmessage will do the work necessary to send the message but it won't send until you connect. Atentu: Vi ne estas nun konektita. Bitmesaĝo faros necesan laboron por sendi mesaĝon, tamen ĝi ne sendos ĝin antaŭ vi konektos. - + Message queued. Mesaĝo envicigita. - + Your 'To' field is empty. Via "Ricevonto"-kampo malplenas. - + Right click one or more entries in your address book and select 'Send message to this address'. Dekstre alklaku kelka(j)n elemento(j)n en via adresaro kaj elektu 'Sendi mesaĝon al tiu adreso'. - + Fetched address from namecoin identity. Venigis adreson de namecoin-a identigo. - + New Message Nova mesaĝo - + From De - + Sending email gateway registration request Sendado de peto pri registrado je retpoŝta kluzo @@ -662,142 +662,142 @@ Estas grava, ke vi faru sekurkopion de tiu dosiero. Ĉu vi volas malfermi la dos La adreso kiun vi enmetis estas malĝusta. Ignoras ĝin. - + Error: You cannot add the same address to your address book twice. Try renaming the existing one if you want. Eraro: Vi ne povas duoble aldoni la saman adreson al via adresaro. Provu renomi la ekzistan se vi volas. - + Error: You cannot add the same address to your subscriptions twice. Perhaps rename the existing one if you want. Eraro: Vi ne povas aldoni duoble la saman adreson al viaj abonoj. Eble renomi la ekzistan se vi volas. - + Restart Restartigi - + You must restart Bitmessage for the port number change to take effect. Vi devas restartigi Bitmesaĝon por ke la ŝanĝo de la numero de pordo (Port Number) efektivigu. - + Bitmessage will use your proxy from now on but you may want to manually restart Bitmessage now to close existing connections (if any). Bitmesaĝo uzos vian prokurilon (proxy) ekde nun, sed eble vi volas permane restartigi Bitmesaĝon nun, por ke ĝi fermu eblajn ekzistajn konektojn. - + Number needed Numero bezonata - + Your maximum download and upload rate must be numbers. Ignoring what you typed. Maksimumaj elŝutrapido kaj alŝutrapido devas esti numeroj. Ignoras kion vi enmetis. - + Will not resend ever Resendos neniam - + Note that the time limit you entered is less than the amount of time Bitmessage waits for the first resend attempt therefore your messages will never be resent. Rigardu, ke la templimon vi enmetis estas pli malgrandan ol tempo dum kiu Bitmesaĝo atendas por resendi unuafoje, do viaj mesaĝoj estos senditaj neniam. - + Sending email gateway unregistration request Sendado de peto pri malregistrado de retpoŝta kluzo - + Sending email gateway status request Sendado de peto pri stato de retpoŝta kluzo - + Passphrase mismatch Pasfrazoj malsamas - + The passphrase you entered twice doesn't match. Try again. La pasfrazo kiun vi duoble enmetis malsamas. Provu denove. - + Choose a passphrase Elektu pasfrazon - + You really do need a passphrase. Vi ja vere bezonas pasfrazon. - + Address is gone Adreso foriris - + Bitmessage cannot find your address %1. Perhaps you removed it? Bitmesaĝo ne povas trovi vian adreson %1. Ĉu eble vi forviŝis ĝin? - + Address disabled Adreso malŝaltita - + Error: The address from which you are trying to send is disabled. You'll have to enable it on the 'Your Identities' tab before using it. Eraro: La adreso kun kiu vi provas sendi estas malŝaltita. Vi devos ĝin ŝalti en la langeto 'Viaj identigoj' antaŭ uzi ĝin. - + Entry added to the Address Book. Edit the label to your liking. Aldonis elementon al adresaro. Redaktu la etikedon laŭvole. - + Entry added to the blacklist. Edit the label to your liking. Aldonis elementon al la nigra listo. Redaktu la etikedon laŭvole. - + Error: You cannot add the same address to your blacklist twice. Try renaming the existing one if you want. Eraro: Vi ne povas duoble aldoni la saman adreson al via nigra listo. Provu renomi la jaman se vi volas. - + Moved items to trash. Movis elementojn al rubujo. - + Undeleted item. Malforviŝis elementon. - + Save As... - Konservi kiel... + Konservi kiel… - + Write error. Skriberaro. - + No addresses selected. Neniu adreso elektita. - + If you delete the subscription, messages that you already received will become inaccessible. Maybe you can consider disabling the subscription instead. Disabled subscriptions will not receive new messages, but you can still view messages you already received. Are you sure you want to delete the subscription? @@ -806,7 +806,7 @@ Are you sure you want to delete the subscription? Ĉu vi certe volas forviŝi la abonon? - + If you delete the channel, messages that you already received will become inaccessible. Maybe you can consider disabling the channel instead. Disabled channels will not receive new messages, but you can still view messages you already received. Are you sure you want to delete the channel? @@ -815,92 +815,92 @@ Are you sure you want to delete the channel? Ĉu vi certe volas forviŝi la kanalon? - + Do you really want to remove this avatar? Ĉu vi certe volas forviŝi tiun ĉi avataron? - + You have already set an avatar for this address. Do you really want to overwrite it? Vi jam agordis avataron por tiu ĉi adreso. Ĉu vi vere volas superskribi ĝin? - + Start-on-login not yet supported on your OS. Starto-dum-ensaluto ne estas ankoraŭ ebla en via operaciumo. - + Minimize-to-tray not yet supported on your OS. Plejetigo al taskopleto ne estas ankoraŭ ebla en via operaciumo. - + Tray notifications not yet supported on your OS. Taskopletaj sciigoj ne estas ankoraŭ eblaj en via operaciumo. - + Testing... - Testado... + Testado… - + This is a chan address. You cannot use it as a pseudo-mailing list. Tio ĉi estas kanaladreso. Vi ne povas ĝin uzi kiel kvazaŭ-dissendolisto. - + The address should start with ''BM-'' La adreso komencu kun "BM-" - + The address is not typed or copied correctly (the checksum failed). La adreso ne estis prave tajpita aŭ kopiita (kontrolsumo malsukcesis). - + The version number of this address is higher than this software can support. Please upgrade Bitmessage. La numero de adresversio estas pli alta ol tiun, kiun la programo poveblas subteni. Bonvolu ĝisdatigi Bitmesaĝon. - + The address contains invalid characters. La adreso enhavas malpermesitajn simbolojn. - + Some data encoded in the address is too short. Kelkaj datumoj koditaj en la adreso estas tro mallongaj. - + Some data encoded in the address is too long. Kelkaj datumoj koditaj en la adreso estas tro longaj. - + Some data encoded in the address is malformed. Kelkaj datumoj koditaj en la adreso estas misformitaj. - + Enter an address above. Enmetu adreson supre. - + Address is an old type. We cannot display its past broadcasts. Malnova speco de adreso. Ne povas montri ĝiajn antaŭajn elsendojn. - + There are no recent broadcasts from this address to display. Neniaj lastatempaj elsendoj de tiu ĉi adreso por montri. - + You are using TCP port %1. (This can be changed in the settings). Vi uzas TCP-pordon %1 (tio ĉi estas ŝanĝebla en la agordoj). @@ -1110,49 +1110,49 @@ Are you sure you want to delete the channel? Aldoni novan elementon - + Display the %1 recent broadcast(s) from this address. Montri la %1 lasta(j)n elsendo(j)n de tiu adreso. - + New version of PyBitmessage is available: %1. Download it from https://github.com/Bitmessage/PyBitmessage/releases/latest La nova versio de PyBitmessage estas disponebla: %1. Elŝutu ĝin de https://github.com/Bitmessage/PyBitmessage/releases/latest - + Waiting for PoW to finish... %1% - Atendado ĝis laborpruvo finos... %1% + Atendado ĝis laborpruvo finos… %1% - + Shutting down Pybitmessage... %1% - Fermado de PyBitmessage... %1% + Fermado de PyBitmessage… %1% - + Waiting for objects to be sent... %1% - Atendado ĝis objektoj estos senditaj... %1% + Atendado ĝis objektoj estos senditaj… %1% - + Saving settings... %1% - Konservado de agordoj... %1% + Konservado de agordoj… %1% - + Shutting down core... %1% - Fermado de kerno... %1% - - - - Stopping notifications... %1% - Haltigado de sciigoj... %1% + Fermado de kerno… %1% + Stopping notifications... %1% + Haltigado de sciigoj… %1% + + + Shutdown imminent... %1% - Fermado tuj... %1% + Fermado tuj… %1% @@ -1165,9 +1165,9 @@ Are you sure you want to delete the channel? %n tago%n tagoj - + Shutting down PyBitmessage... %1% - Fermado de PyBitmessage... %1% + Fermado de PyBitmessage… %1% @@ -1182,7 +1182,7 @@ Are you sure you want to delete the channel? Done generating address. Doing work necessary to broadcast it... - Adreso kreita. Kalkulado de laborpruvo, kiu endas por elsendi ĝin... + Adreso kreita. Kalkulado de laborpruvo, kiu endas por elsendi ĝin… @@ -1215,61 +1215,61 @@ Are you sure you want to delete the channel? Atentu: Via disko aŭ subdisko estas plenplena. Bitmesaĝo fermiĝos. - + Error! Could not find sender address (your address) in the keys.dat file. Eraro! Ne povas trovi adreson de sendanto (vian adreson) en la dosiero keys.dat. - + Doing work necessary to send broadcast... - Kalkulado de laborpruvo, kiu endas por sendi elsendon... + Kalkulado de laborpruvo, kiu endas por sendi elsendon… - + Broadcast sent on %1 Elsendo sendita je %1 - + Encryption key was requested earlier. Peto pri ĉifroŝlosilo jam sendita. - + Sending a request for the recipient's encryption key. Sendado de peto pri ĉifroŝlosilo de ricevonto. - + Looking up the receiver's public key Serĉado de publika ĉifroŝlosilo de ricevonto - + Problem: Destination is a mobile device who requests that the destination be included in the message but this is disallowed in your settings. %1 Eraro: celadreso estas portebla aparato kiu necesas, ke la celadreso estu enhavita en la mesaĝo, sed tio estas malpermesita ne viaj agordoj. %1 - + Doing work necessary to send message. There is no required difficulty for version 2 addresses like this. Kalkulado de laborpruvo, kiu endas por sendi mesaĝon. Malfacilaĵo ne estas bezonata por adresoj versioj 2, kiel tiu ĉi adreso. - + Doing work necessary to send message. Receiver's required difficulty: %1 and %2 Kalkulado de laborpruvo, kiu endas por sendi mesaĝon. Ricevonto postulas malfacilaĵon: %1 kaj %2 - + Problem: The work demanded by the recipient (%1 and %2) is more difficult than you are willing to do. %3 Eraro: la demandita laboro de la ricevonto (%1 kaj %2) estas pli malfacila ol vi pretas fari. %3 - + Problem: You are trying to send a message to yourself or a chan but your encryption key could not be found in the keys.dat file. Could not encrypt message. %1 Eraro: Vi provis sendi mesaĝon al vi mem aŭ al kanalo, tamen via ĉifroŝlosilo ne estas trovebla en la dosiero keys.dat. Mesaĝo ne povis esti ĉifrita. %1 @@ -1279,7 +1279,7 @@ Ricevonto postulas malfacilaĵon: %1 kaj %2 Kalkulado de laborpruvo, kiu endas por sendi mesaĝon. - + Message sent. Waiting for acknowledgement. Sent on %1 Mesaĝo sendita. Atendado je konfirmo. Sendita je %1 @@ -1289,12 +1289,12 @@ Ricevonto postulas malfacilaĵon: %1 kaj %2 Kalkulado de laborpruvo, kiu endas por peti pri ĉifroŝlosilo. - + Broadcasting the public key request. This program will auto-retry if they are offline. Elsendado de peto pri publika ĉifroŝlosilo. La programo reprovos se ili estas eksterrete. - + Sending public key request. Waiting for reply. Requested at %1 Sendado de peto pri publika ĉifroŝlosilo. Atendado je respondo. Petis je %1 @@ -1314,7 +1314,7 @@ Ricevonto postulas malfacilaĵon: %1 kaj %2 Marki ĉiujn mesaĝojn kiel legitajn - + Are you sure you would like to mark all messages read? Ĉu vi certe volas marki ĉiujn mesaĝojn kiel legitajn? @@ -1324,37 +1324,37 @@ Ricevonto postulas malfacilaĵon: %1 kaj %2 Kalkulado de laborpruvo, kiu endas por sendi elsendon. - + Proof of work pending Laborpruvo haltigita - + %n object(s) pending proof of work Haltigis laborpruvon por %n objektoHaltigis laborpruvon por %n objektoj - + %n object(s) waiting to be distributed %n objekto atendas je sendato%n objektoj atendas je sendato - + Wait until these tasks finish? Ĉu atendi ĝis tiujn taskojn finos? - + Problem communicating with proxy: %1. Please check your network settings. Eraro dum komunikado kun prokurilo: %1. Bonvolu kontroli viajn retajn agordojn. - + SOCKS5 Authentication problem: %1. Please check your SOCKS5 settings. Eraro dum SOCKS5 aŭtentigado: %1. Bonvolu kontroli viajn SOCKS5-agordojn. - + The time on your computer, %1, may be wrong. Please verify your settings. La horloĝo de via komputilo, %1, eble eraras. Bonvolu kontroli viajn agordojn. @@ -1423,79 +1423,79 @@ Bonvenon al facila kaj sekura Bitmesaĝo malkonsilinda por kanaloj - + Problems connecting? Try enabling UPnP in the Network Settings Ĉu problemo kun konektado? Provu aktivigi UPnP en retaj agordoj. - + Error: Bitmessage addresses start with BM- Please check the recipient address %1 Eraro: bitmesaĝaj adresoj komenciĝas kun BM-. Bonvolu kontroli la adreson de ricevonto %1 - + Error: The recipient address %1 is not typed or copied correctly. Please check it. Eraro: la adreso de ricevonto %1 estas malprave tajpita aŭ kopiita. Bonvolu kontroli ĝin. - + Error: The recipient address %1 contains invalid characters. Please check it. Eraro: la adreso de ricevonto %1 enhavas malpermesatajn simbolojn. Bonvolu kontroli ĝin. - + Error: The version of the recipient address %1 is too high. Either you need to upgrade your Bitmessage software or your acquaintance is being clever. Eraro: la versio de adreso de ricevonto %1 estas tro alta. Eble vi devas ĝisdatigi vian bitmesaĝan programon aŭ via sagaca konato uzas alian programon. - + Error: Some data encoded in the recipient address %1 is too short. There might be something wrong with the software of your acquaintance. Eraro: kelkaj datumoj koditaj en la adreso de ricevonto %1 estas tro mallongaj. Povus esti ke io en la programo de via konato malfunkcias. - + Error: Some data encoded in the recipient address %1 is too long. There might be something wrong with the software of your acquaintance. Eraro: kelkaj datumoj koditaj en la adreso de ricevonto %1 estas tro longaj. Povus esti ke io en la programo de via konato malfunkcias. - + Error: Some data encoded in the recipient address %1 is malformed. There might be something wrong with the software of your acquaintance. Eraro: kelkaj datumoj koditaj en la adreso de ricevonto %1 estas misformitaj. Povus esti ke io en la programo de via konato malfunkcias. - + Error: Something is wrong with the recipient address %1. Eraro: io malĝustas kun la adreso de ricevonto %1. - + Synchronisation pending Samtempigado haltigita - + Bitmessage hasn't synchronised with the network, %n object(s) to be downloaded. If you quit now, it may cause delivery delays. Wait until the synchronisation finishes? Bitmesaĝo ne estas samtempigita kun la reto, %n objekto elŝutendas. Se vi eliros nun, tio povas igi malfruiĝojn de liveradoj. Ĉu atendi ĝis la samtempigado finiĝos?Bitmesaĝo ne estas samtempigita kun la reto, %n objektoj elŝutendas. Se vi eliros nun, tio povas igi malfruiĝojn de liveradoj. Ĉu atendi ĝis la samtempigado finiĝos? - + Not connected Nekonektita - + Bitmessage isn't connected to the network. If you quit now, it may cause delivery delays. Wait until connected and the synchronisation finishes? Bitmesaĝo ne estas konektita al la reto. Se vi eliros nun, tio povas igi malfruiĝojn de liveradoj. Ĉu atendi ĝis ĝi konektos kaj la samtempigado finiĝos? - + Waiting for network connection... - Atendado je retkonekto... + Atendado je retkonekto… - + Waiting for finishing synchronisation... - Atendado ĝis samtempigado finiĝos... + Atendado ĝis samtempigado finiĝos… @@ -1524,14 +1524,14 @@ Bonvenon al facila kaj sekura Bitmesaĝo MsgDecode - + The message has an unknown encoding. Perhaps you should upgrade Bitmessage. La mesaĝo enhavas nekonatan kodoprezenton. Eble vi devas ĝisdatigi Bitmesaĝon. - + Unknown encoding Nekonata kodoprezento @@ -1840,110 +1840,125 @@ La 'hazardnombra' adreso estas antaŭagordita, sed antaŭkalkuleblaj a Ĉiuj konektoj: - + Since startup: Ekde starto: - + Processed 0 person-to-person messages. Pritraktis 0 inter-personajn mesaĝojn. - + Processed 0 public keys. Pritraktis 0 publikajn ŝlosilojn. - + Processed 0 broadcasts. Pritraktis 0 elsendojn. - + Inventory lookups per second: 0 Petoj pri inventaro en sekundo: 0 - + Objects to be synced: Samtempigotaj eroj: - + Stream # Fluo # Connections - Konektoj + - + Since startup on %1 Ekde lanĉo de la programo je %1 - + Down: %1/s Total: %2 Elŝuto: %1/s Sume: %2 - + Up: %1/s Total: %2 Alŝuto: %1/s Sume: %2 - + Total Connections: %1 Ĉiuj konektoj: %1 - + Inventory lookups per second: %1 Petoj pri inventaro en sekundo: %1 - + Up: 0 kB/s Alŝuto: 0 kB/s - + Down: 0 kB/s Elŝuto: 0 kB/s Network Status - Reta Stato + Reta stato - + byte(s) bitokobitokoj - + Object(s) to be synced: %n Objekto por samtempigi: %nObjektoj por samtempigi: %n - + Processed %n person-to-person message(s). Pritraktis %n inter-personan mesaĝon.Pritraktis %n inter-personajn mesaĝojn. - + Processed %n broadcast message(s). Pritraktis %n elsendon.Pritraktis %n elsendojn. - + Processed %n public key(s). Pritraktis %n publikan ŝlosilon.Pritraktis %n publikajn ŝlosilojn. + + + Peer + Samtavolano + + + + User agent + Klienta aplikaĵo + + + + TLS + TLS + newChanDialog From 20e01860cf8b7927c2829fa5b8ad178d9fd0334b Mon Sep 17 00:00:00 2001 From: Peter Surda Date: Tue, 27 Jun 2017 13:16:41 +0200 Subject: [PATCH 124/407] Network status peer list shouldn't be editable --- src/bitmessageqt/networkstatus.ui | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/bitmessageqt/networkstatus.ui b/src/bitmessageqt/networkstatus.ui index e4264124..993cf1c6 100644 --- a/src/bitmessageqt/networkstatus.ui +++ b/src/bitmessageqt/networkstatus.ui @@ -85,6 +85,9 @@ QFrame::Plain + + QAbstractItemView::NoEditTriggers + false From f5a143d0b83387b2cad2f320f7067d95375ca498 Mon Sep 17 00:00:00 2001 From: Peter Surda Date: Tue, 27 Jun 2017 13:19:12 +0200 Subject: [PATCH 125/407] Config validator - config options can have validators - limit maxoutboundconnections to max 8 --- src/bitmessageqt/settings.py | 2 +- src/bmconfigparser.py | 31 +++++++++++++++++++++++++++++++ 2 files changed, 32 insertions(+), 1 deletion(-) diff --git a/src/bitmessageqt/settings.py b/src/bitmessageqt/settings.py index 300c3544..4342fd09 100644 --- a/src/bitmessageqt/settings.py +++ b/src/bitmessageqt/settings.py @@ -165,7 +165,7 @@ class Ui_settingsDialog(object): self.lineEditMaxOutboundConnections.setSizePolicy(sizePolicy) self.lineEditMaxOutboundConnections.setMaximumSize(QtCore.QSize(60, 16777215)) self.lineEditMaxOutboundConnections.setObjectName(_fromUtf8("lineEditMaxOutboundConnections")) - self.lineEditMaxOutboundConnections.setValidator(QtGui.QIntValidator(0, 4096, self.lineEditMaxOutboundConnections)) + self.lineEditMaxOutboundConnections.setValidator(QtGui.QIntValidator(0, 8, self.lineEditMaxOutboundConnections)) self.gridLayout_9.addWidget(self.lineEditMaxOutboundConnections, 2, 2, 1, 1) self.gridLayout_4.addWidget(self.groupBox_3, 2, 0, 1, 1) self.groupBox_2 = QtGui.QGroupBox(self.tabNetworkSettings) diff --git a/src/bmconfigparser.py b/src/bmconfigparser.py index 6725a4d8..913df9c0 100644 --- a/src/bmconfigparser.py +++ b/src/bmconfigparser.py @@ -37,6 +37,8 @@ class BMConfigParser(ConfigParser.SafeConfigParser): if self._optcre is self.OPTCRE or value: if not isinstance(value, basestring): raise TypeError("option values must be strings") + if not self.validate(section, option, value): + raise ValueError("Invalid value %s" % str(value)) return ConfigParser.ConfigParser.set(self, section, option, value) def get(self, section, option, raw=False, variables=None): @@ -76,6 +78,20 @@ class BMConfigParser(ConfigParser.SafeConfigParser): def addresses(self): return filter(lambda x: x.startswith('BM-'), BMConfigParser().sections()) + def read(self, filenames): + ConfigParser.ConfigParser.read(self, filenames) + for section in self.sections(): + for option in self.options(section): + try: + if not self.validate(section, option, ConfigParser.ConfigParser.get(self, section, option)): + try: + newVal = BMConfigDefaults[section][option] + except KeyError: + continue + ConfigParser.ConfigParser.set(self, section, option, newVal) + except ConfigParser.InterpolationError: + continue + def save(self): fileName = os.path.join(state.appdata, 'keys.dat') fileNameBak = fileName + "." + datetime.datetime.now().strftime("%Y%j%H%M%S%f") + '.bak' @@ -93,3 +109,18 @@ class BMConfigParser(ConfigParser.SafeConfigParser): # delete the backup if fileNameExisted: os.remove(fileNameBak) + + def validate(self, section, option, value): + try: + return getattr(self, "validate_%s_%s" % (section, option))(value) + except AttributeError: + return True + + def validate_bitmessagesettings_maxoutboundconnections(self, value): + try: + value = int(value) + except ValueError: + return False + if value < 0 or value > 8: + return False + return True From cc3cf777596f43be63310daeae804bd640b015f9 Mon Sep 17 00:00:00 2001 From: Peter Surda Date: Tue, 27 Jun 2017 13:25:12 +0200 Subject: [PATCH 126/407] New class multiqueue - to be used for invthread and addthread - updated invthread for multiqueue --- src/multiqueue.py | 35 +++++++++++++++++++++ src/network/invthread.py | 68 +++++++++------------------------------- src/queues.py | 5 ++- 3 files changed, 53 insertions(+), 55 deletions(-) create mode 100644 src/multiqueue.py diff --git a/src/multiqueue.py b/src/multiqueue.py new file mode 100644 index 00000000..30326ee7 --- /dev/null +++ b/src/multiqueue.py @@ -0,0 +1,35 @@ +from collections import deque +import Queue +import random + +class MultiQueue(Queue.Queue): + defaultQueueCount = 10 + def __init__(self, maxsize=0, count=0): + if not count: + self.queueCount = MultiQueue.defaultQueueCount + else: + self.queueCount = count + Queue.Queue.__init__(self, maxsize) + + # Initialize the queue representation + def _init(self, maxsize): + self.iter = 0 + self.queues = [] + for i in range(self.queueCount): + self.queues.append(deque()) + + def _qsize(self, len=len): + return len(self.queues[self.iter]) + + # Put a new item in the queue + def _put(self, item): + #self.queue.append(item) + i = random.randrange(0, self.queueCount) + self.queues[i].append((item)) + + # Get an item from the queue + def _get(self): + return self.queues[self.iter].popleft() + + def iterate(self): + self.iter = (self.iter + 1) % self.queueCount diff --git a/src/network/invthread.py b/src/network/invthread.py index 9d05aec4..398fecf0 100644 --- a/src/network/invthread.py +++ b/src/network/invthread.py @@ -1,37 +1,22 @@ -from binascii import hexlify -import collections import Queue -import random import threading -import time import addresses -from bmconfigparser import BMConfigParser -from debug import logger from helper_threading import StoppableThread -from network.bmproto import BMProto from network.connectionpool import BMConnectionPool from queues import invQueue import protocol import state class InvThread(threading.Thread, StoppableThread): - size = 10 - def __init__(self): threading.Thread.__init__(self, name="InvThread") self.initStop() self.name = "InvThread" - self.shutdown = False - - self.collectionOfInvs = [] - for i in range(InvThread.size): - self.collectionOfInvs.append({}) - def run(self): - iterator = 0 while not state.shutdown: + chunk = [] while True: try: data = invQueue.get(False) @@ -39,50 +24,25 @@ class InvThread(threading.Thread, StoppableThread): BMConnectionPool().handleReceivedObject(data[0], data[1]) else: BMConnectionPool().handleReceivedObject(data[0], data[1], data[2]) - self.holdHash (data[0], data[1]) + chunk.append((data[0], data[1])) except Queue.Empty: break - if self.collectionOfInvs[iterator]: - for connection in BMConnectionPool().inboundConnections.values() + BMConnectionPool().outboundConnections.values(): + if chunk: + for connection in BMConnectionPool().inboundConnections.values() + \ + BMConnectionPool().outboundConnections.values(): hashes = [] - for stream in connection.streams: + for inv in chunk: + if inv[0] not in connection.streams: + continue try: - for hashId in self.collectionOfInvs[iterator][stream]: - try: - with connection.objectsNewToThemLock: - del connection.objectsNewToThem[hashId] - hashes.append(hashId) - except KeyError: - pass + with connection.objectsNewToThemLock: + del connection.objectsNewToThem[inv[1]] + hashes.append(inv[1]) except KeyError: continue if hashes: - connection.writeQueue.put(protocol.CreatePacket('inv', addresses.encodeVarint(len(hashes)) + "".join(hashes))) - self.collectionOfInvs[iterator] = {} - iterator += 1 - iterator %= InvThread.size + connection.writeQueue.put(protocol.CreatePacket('inv', \ + addresses.encodeVarint(len(hashes)) + "".join(hashes))) + invQueue.iterate() self.stop.wait(1) - - def holdHash(self, stream, hashId): - i = random.randrange(0, InvThread.size) - if stream not in self.collectionOfInvs[i]: - self.collectionOfInvs[i][stream] = [] - self.collectionOfInvs[i][stream].append(hashId) - - def hasHash(self, hashId): - for streamlist in self.collectionOfInvs: - for stream in streamlist: - if hashId in streamlist[stream]: - return True - return False - - def hashCount(self): - retval = 0 - for streamlist in self.collectionOfInvs: - for stream in streamlist: - retval += len(streamlist[stream]) - return retval - - def close(self): - self.shutdown = True diff --git a/src/queues.py b/src/queues.py index 7c36d54a..223c7c3b 100644 --- a/src/queues.py +++ b/src/queues.py @@ -1,12 +1,15 @@ import Queue + from class_objectProcessorQueue import ObjectProcessorQueue +from multiqueue import MultiQueue workerQueue = Queue.Queue() UISignalQueue = Queue.Queue() addressGeneratorQueue = Queue.Queue() # receiveDataThreads dump objects they hear on the network into this queue to be processed. objectProcessorQueue = ObjectProcessorQueue() -invQueue = Queue.Queue() +invQueue = MultiQueue() +addrQueue = MultiQueue() portCheckerQueue = Queue.Queue() peerDiscoveryQueue = Queue.Queue() apiAddressGeneratorReturnQueue = Queue.Queue( From 70c5929e92c7c3d6635d1c159de3fdd89013d0eb Mon Sep 17 00:00:00 2001 From: PyBitmessage Translations Date: Tue, 4 Jul 2017 17:51:58 +0200 Subject: [PATCH 127/407] Auto-updated language ru from transifex --- src/translations/bitmessage_ru.qm | Bin 91913 -> 91957 bytes src/translations/bitmessage_ru.ts | 309 ++++++++++++++++-------------- 2 files changed, 162 insertions(+), 147 deletions(-) diff --git a/src/translations/bitmessage_ru.qm b/src/translations/bitmessage_ru.qm index f6e79656161ac4b6782468ae110b55ca9d5fa6b8..b0d1d886b8566e42577ef17983b78da1bd80f8cd 100644 GIT binary patch delta 3686 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 delta 3644 zcmZ`*d0bRw7k=Kk_s(|j+)2J8k{KDyqP zkO>?b0=w(&z|6j^^FaLPtj~}WV6My90Lc!h_hl|K8-dpe%w;M4@4#I) zJc$AftYhw7N`Q-`s$#JB?*#1)h2z;Gn2?aJaJ)k$PY6;tuUHF|S15+QS_`JU zqwu(s3@kA#0@sjTI!h5V<|Sd+s#xfF49FRxShUFqj2fpft&Rnpm5Q|5E?_;|6zOlz z4=FMhHv<)u6@@eFfdLB@Uxd^9QFj$(G6ne5T2VE#gfLC}K~cM?FR&s`adY)~BFlco zEqNB09Ikl4F9n*Wa#FfLWI4j=t{wpfyKwsT1X)uZXL;m4bx$JKJKqa9Va3^v+6(s1 zEY8u7+O;g0bG}|m0V+6GiSD(!z>SDa0vle=c^Z0yO`OR2M4IY=bDOx}I36hN!^N~w z=|5`c5}fV;wwJlwnlhm3ESK9B0p!|o1rh5h(1F}Z=>i2hpR3$P&vDsYwJI8LdfaJS z6x;^eEm-qb4-OsC`Zk<=5T>elNBi5($hX+!ir91h<4*Tf&6mKfa?+P3_$e%pu z1=dU8ziS#uj^p_Y3HCt9V!qbm9G&~~x6V8#Qtjq{Utt4$d0t?@IsqQC;Fv=;ze*VS zbUU!DMR2Qp3MRA)?lDBt9jgW37Y1PT2H}G{H;DCmAtH&mvn5=J3MFV$=a_`KuHO+x zB|>Zgm3qumVU0^Qn3F+BSrZL5cCL`&{>Fi)u-$DSa05c#^QB;YhY342k;6>s}tRs%8REN^W`mEaLO*K9va?Y`?u_#ob=Fyb!fqQC^btB z6hL&QCi$OsFv&`j9!!qiEH&nLcp!U^X3Jm0Xg8?QS1cH zd)lZxs%SrhHo9pXnC}&BOcePGnWUY6hXQTL)tcUXVRpXSq;MKOBhG8{9+Jaq2W^R0 zE3tK$wk))r0{T&VLVQ61KG2@6rp)9M+Vg9uUGtgtrw!h~g7ezD;SHpe7TUkIQD7t8 zL^i&JhEAiXNuVz9DHpZeRbX9TQClBRnLieF$+Xb)xNH)w@{B}^QnBBQ)4-q`;(${T zgkg&4tV{xaREl2H==W)TMQ_U4xnJX^aDv@n& z`t18`Vrh42i#L&DA}4K`9Rl1tD;-$*oEDCI(h&=~sLw;`h$mrM`anAJrW+3XNae9a z-ajE#u%#rec5f{k+bEsc>I1Bgld2yk0e)+wpXM8YzY?UY`etC|W2x?CFPPYvQPQuU zUZ?RoNqVu7u*&#Kr+#pZu$-amxsXWp+aJ2VbI#M&dr{XfmmDmP(m9$B0#63%oYN|Z zE4y@VEvIQLC+kLAcmd=6b^c+&v^m%6rg*di!5+E@CnFFRsEgd6OBLv*ThKxQ{a~px z9yOYP%i+3MQz&IPRJY16i^wuxmzd~9mAI!X_@x@Sx59&tu#3srPh8@H;TREsD z4a~knUiG7ihL0gg-oQ#oYB@RU3B6cikaLe7AO~J@ULN&pO_987#wgPIIC*z(s>tR> z`Nw=WpuIt^9zvu9I&J^VPW#xV)3&6@bq3m~9{l#!vcA>wlZkz40qKz2ZDg9Z75X72 zdutM$5qcNnGTLTS^sbj|z}%hm9zD0vDz#ky!8cT@!^_%SR*Ywf=B(sy<_c3d=gB*pt=9OKbxlsRWme|RyDZIfljO3JT?PG_ z@3uZ8SvPZ1&2Y|(_ca$*cV{l*32Z1Dd8W#v$& diff --git a/src/translations/bitmessage_ru.ts b/src/translations/bitmessage_ru.ts index 19b1ddcc..3cd8fc43 100644 --- a/src/translations/bitmessage_ru.ts +++ b/src/translations/bitmessage_ru.ts @@ -58,12 +58,12 @@ EmailGatewayRegistrationDialog - + Registration failed: Регистрация не удалась: - + The requested email address is not available, please try a new one. Fill out the new desired email address (including @mailchuck.com) below: Запрашиваемый адрес email недоступен, попробуйте ввести другой. Введите желаемый адрес (включая @mailchuck.com) ниже: @@ -315,10 +315,10 @@ Please type the desired email address (including @mailchuck.com) below: Acknowledgement of the message received %1 - Сообщение доставлено в %1 + Доставлено в %1 - + Broadcast queued. Рассылка ожидает очереди. @@ -348,7 +348,7 @@ Please type the desired email address (including @mailchuck.com) below: Неизвестный статус: %1 %2 - + Not Connected Не соединено @@ -380,7 +380,7 @@ Please type the desired email address (including @mailchuck.com) below: You may manage your keys by editing the keys.dat file stored in the same directory as this program. It is important that you back up this file. - Вы можете управлять Вашими ключами, отредактировав файл keys.dat, находящийся в той же папке, что и эта программа. + Вы можете управлять Вашими ключами, редактируя файл keys.dat, находящийся в той же папке, что и эта программа. Создайте резервную копию этого файла перед тем как будете его редактировать. @@ -388,7 +388,7 @@ Please type the desired email address (including @mailchuck.com) below: You may manage your keys by editing the keys.dat file stored in %1 It is important that you back up this file. - Вы можете управлять Вашими ключами, отредактировав файл keys.dat, находящийся в + Вы можете управлять Вашими ключами, редактируя файл keys.dat, находящийся в %1 Создайте резервную копию этого файла перед тем как будете его редактировать. @@ -400,7 +400,7 @@ It is important that you back up this file. You may manage your keys by editing the keys.dat file stored in the same directory as this program. It is important that you back up this file. Would you like to open the file now? (Be sure to close Bitmessage before making any changes.) - Вы можете управлять Вашими ключами, отредактировав файл keys.dat, находящийся в той же папке, что и эта программа. + Вы можете управлять Вашими ключами, редактируя файл keys.dat, находящийся в той же папке, что и эта программа. Создайте резервную копию этого файла перед тем как будете его редактировать. Хотели бы Вы открыть этот файл сейчас? (пожалуйста, закройте Bitmessage до того как Вы внесёте в этот файл какие-либо изменения.) @@ -409,7 +409,7 @@ It is important that you back up this file. You may manage your keys by editing the keys.dat file stored in %1 It is important that you back up this file. Would you like to open the file now? (Be sure to close Bitmessage before making any changes.) - Вы можете управлять Вашими ключами, отредактировав файл keys.dat, находящийся в + Вы можете управлять Вашими ключами, редактируя файл keys.dat, находящийся в %1 Создайте резервную копию этого файла перед тем как будете его редактировать. Хотели бы Вы открыть этот файл сейчас? (пожалуйста, закройте Bitmessage до того как Вы внесёте в этот файл какие-либо изменения.) @@ -515,22 +515,22 @@ It is important that you back up this file. Would you like to open the file now? - + Connection lost Соединение потеряно - + Connected Соединено - + Message trashed Сообщение удалено - + The TTL, or Time-To-Live is the length of time that the network will hold the message. The recipient must get it during this time. If your Bitmessage client does not hear an acknowledgement, it will resend the message automatically. The longer the Time-To-Live, the @@ -541,17 +541,17 @@ It is important that you back up this file. Would you like to open the file now? сообщение. Часто разумным вариантом будет установка TTL на 4 или 5 дней. - + Message too long Сообщение слишком длинное - + The message that you are trying to send is too long by %1 bytes. (The maximum is 261644 bytes). Please cut it down before sending. Сообщение, которое вы пытаетесь отправить, длиннее максимально допустимого на %1 байт. (Максимально допустимое значение 261644 байта). Пожалуйста, сократите сообщение перед отправкой. - + Error: Your account wasn't registered at an email gateway. Sending registration now as %1, please wait for the registration to be processed before retrying sending. Ошибка: ваш аккаунт не зарегистрирован на Email-шлюзе. Отправка регистрации %1, пожалуйста, подождите пока процесс регистрации не завершится, прежде чем попытаться отправить сообщение заново. @@ -596,67 +596,67 @@ It is important that you back up this file. Would you like to open the file now? - + Error: You must specify a From address. If you don't have one, go to the 'Your Identities' tab. Вы должны указать адрес в поле "От кого". Вы можете найти Ваш адрес во вкладке "Ваши Адреса". - + Address version number Версия адреса - + Concerning the address %1, Bitmessage cannot understand address version numbers of %2. Perhaps upgrade Bitmessage to the latest version. По поводу адреса %1: Bitmessage не поддерживаем адреса версии %2. Возможно, Вам нужно обновить клиент Bitmessage. - + Stream number Номер потока - + Concerning the address %1, Bitmessage cannot handle stream numbers of %2. Perhaps upgrade Bitmessage to the latest version. По поводу адреса %1: Bitmessage не поддерживаем стрим номер %2. Возможно, Вам нужно обновить клиент Bitmessage. - + Warning: You are currently not connected. Bitmessage will do the work necessary to send the message but it won't send until you connect. Внимание: Вы не подключены к сети. Bitmessage выполнит работу, требуемую для отправки сообщения, но не отправит его до тех пор, пока Вы не подключитесь. - + Message queued. Сообщение в очереди. - + Your 'To' field is empty. Вы не заполнили поле 'Кому'. - + Right click one or more entries in your address book and select 'Send message to this address'. Нажмите правую кнопку мыши на каком-либо адресе и выберите "Отправить сообщение на этот адрес". - + Fetched address from namecoin identity. Получить адрес через Namecoin. - + New Message Новое сообщение - + From От - + Sending email gateway registration request Отправка запроса на регистрацию на Email-шлюзе @@ -671,142 +671,142 @@ It is important that you back up this file. Would you like to open the file now? Вы ввели неправильный адрес. Это адрес проигнорирован. - + Error: You cannot add the same address to your address book twice. Try renaming the existing one if you want. Ошибка: Вы не можете добавлять один и тот же адрес в Адресную Книгу несколько раз. Попробуйте переименовать существующий адрес. - + Error: You cannot add the same address to your subscriptions twice. Perhaps rename the existing one if you want. Ошибка: вы не можете добавить один и тот же адрес в ваши подписки дважды. Пожалуйста, переименуйте имеющийся адрес, если хотите. - + Restart Перезапустить - + You must restart Bitmessage for the port number change to take effect. Вы должны перезапустить Bitmessage, чтобы смена номера порта имела эффект. - + Bitmessage will use your proxy from now on but you may want to manually restart Bitmessage now to close existing connections (if any). Bitmessage будет использовать Ваш прокси, начиная прямо сейчас. Тем не менее Вам имеет смысл перезапустить Bitmessage, чтобы закрыть уже существующие соединения. - + Number needed Требуется число - + Your maximum download and upload rate must be numbers. Ignoring what you typed. Скорости загрузки и выгрузки должны быть числами. Игнорирую то, что вы набрали. - + Will not resend ever Не пересылать никогда - + Note that the time limit you entered is less than the amount of time Bitmessage waits for the first resend attempt therefore your messages will never be resent. Обратите внимание, что лимит времени, который вы ввели, меньше чем время, которое Bitmessage ждет перед первой попыткой переотправки сообщения, поэтому ваши сообщения никогда не будут переотправлены. - + Sending email gateway unregistration request Отправка запроса на отмену регистрации на Email-шлюзе - + Sending email gateway status request Отправка запроса статуса аккаунта на Email-шлюзе - + Passphrase mismatch Секретная фраза не подходит - + The passphrase you entered twice doesn't match. Try again. Вы ввели две разные секретные фразы. Пожалуйста, повторите заново. - + Choose a passphrase Придумайте секретную фразу - + You really do need a passphrase. Вы действительно должны ввести секретную фразу. - + Address is gone Адрес утерян - + Bitmessage cannot find your address %1. Perhaps you removed it? Bitmessage не может найти Ваш адрес %1. Возможно Вы удалили его? - + Address disabled Адрес выключен - + Error: The address from which you are trying to send is disabled. You'll have to enable it on the 'Your Identities' tab before using it. Ошибка: адрес, с которого Вы пытаетесь отправить, выключен. Вам нужно будет включить этот адрес во вкладке "Ваши адреса". - + Entry added to the Address Book. Edit the label to your liking. Запись добавлена в Адресную Книгу. Вы можете её отредактировать. - + Entry added to the blacklist. Edit the label to your liking. Запись добавлена в чёрный список. Измените название по своему вкусу. - + Error: You cannot add the same address to your blacklist twice. Try renaming the existing one if you want. Ошибка: вы не можете добавить один и тот же адрес в чёрный список дважды. Попробуйте переименовать существующий адрес. - + Moved items to trash. Удалено в корзину. - + Undeleted item. Отменить удаление записи - + Save As... Сохранить как ... - + Write error. Ошибка записи. - + No addresses selected. Вы не выбрали адрес. - + If you delete the subscription, messages that you already received will become inaccessible. Maybe you can consider disabling the subscription instead. Disabled subscriptions will not receive new messages, but you can still view messages you already received. Are you sure you want to delete the subscription? @@ -815,7 +815,7 @@ Are you sure you want to delete the subscription? Вы уверены, что хотите отменить подписку? - + If you delete the channel, messages that you already received will become inaccessible. Maybe you can consider disabling the channel instead. Disabled channels will not receive new messages, but you can still view messages you already received. Are you sure you want to delete the channel? @@ -824,92 +824,92 @@ Are you sure you want to delete the channel? Вы уверены, что хотите удалить канал? - + Do you really want to remove this avatar? Вы уверены, что хотите удалить этот аватар? - + You have already set an avatar for this address. Do you really want to overwrite it? У вас уже есть аватар для этого адреса. Вы уверены, что хотите перезаписать аватар? - + Start-on-login not yet supported on your OS. Запуск программы при входе в систему ещё не поддерживается в вашей операционной системе. - + Minimize-to-tray not yet supported on your OS. Сворачивание в трей ещё не поддерживается в вашей операционной системе. - + Tray notifications not yet supported on your OS. Уведомления в трее ещё не поддерживаеются в вашей операционной системе. - + Testing... Проверяем... - + This is a chan address. You cannot use it as a pseudo-mailing list. Это адрес chan-а. Вы не можете его использовать как адрес рассылки. - + The address should start with ''BM-'' Адрес должен начинаться с "BM-" - + The address is not typed or copied correctly (the checksum failed). Адрес введен или скопирован неверно (контрольная сумма не сходится). - + The version number of this address is higher than this software can support. Please upgrade Bitmessage. Версия этого адреса более поздняя, чем Ваша программа. Пожалуйста, обновите программу Bitmessage. - + The address contains invalid characters. Адрес содержит запрещённые символы. - + Some data encoded in the address is too short. Данные, закодированные в адресе, слишком короткие. - + Some data encoded in the address is too long. Данные, закодированные в адресе, слишком длинные. - + Some data encoded in the address is malformed. Данные, закодированные в адресе, имеют неверный формат. - + Enter an address above. Введите адрес выше. - + Address is an old type. We cannot display its past broadcasts. Адрес старого типа. Мы не можем отобразить его прошлые рассылки. - + There are no recent broadcasts from this address to display. Нет недавних рассылок с этого адреса для отображения. - + You are using TCP port %1. (This can be changed in the settings). Вы используете TCP порт %1. (Его можно поменять в настройках). @@ -1119,47 +1119,47 @@ Are you sure you want to delete the channel? Добавить новую запись - + Display the %1 recent broadcast(s) from this address. Показать %1 прошлых рассылок с этого адреса. - + New version of PyBitmessage is available: %1. Download it from https://github.com/Bitmessage/PyBitmessage/releases/latest Доступна новая версия PyBitmessage: %1. Загрузите её: https://github.com/Bitmessage/PyBitmessage/releases/latest - + Waiting for PoW to finish... %1% Ожидание окончания PoW... %1% - + Shutting down Pybitmessage... %1% Завершение PyBitmessage... %1% - + Waiting for objects to be sent... %1% Ожидание отправки объектов... %1% - + Saving settings... %1% Сохранение настроек... %1% - + Shutting down core... %1% Завершение работы ядра... %1% - + Stopping notifications... %1% Остановка сервиса уведомлений... %1% - + Shutdown imminent... %1% Завершение вот-вот произойдет... %1% @@ -1174,14 +1174,14 @@ Are you sure you want to delete the channel? %n день%n дня%n дней%n дней - + Shutting down PyBitmessage... %1% Завершение PyBitmessage... %1% Sent - Отправленные + Отправлено @@ -1224,61 +1224,61 @@ Are you sure you want to delete the channel? Внимание: свободное место на диске закончилось. Bitmessage завершит свою работу. - + Error! Could not find sender address (your address) in the keys.dat file. Ошибка: невозможно найти адрес отправителя (ваш адрес) в файле ключей keys.dat - + Doing work necessary to send broadcast... Выполнение работы, требуемой для рассылки... - + Broadcast sent on %1 Рассылка отправлена на %1 - + Encryption key was requested earlier. Ключ шифрования запрошен ранее. - + Sending a request for the recipient's encryption key. Отправка запроса ключа шифрования получателя. - + Looking up the receiver's public key Поиск открытого ключа получателя - + Problem: Destination is a mobile device who requests that the destination be included in the message but this is disallowed in your settings. %1 Проблема: адресат является мобильным устройством, которое требует, чтобы адрес назначения был включен в сообщение, однако, это запрещено в ваших настройках. %1 - + Doing work necessary to send message. There is no required difficulty for version 2 addresses like this. Выполнение работы, требуемой для отправки сообщения. Для адреса версии 2 (как этот), не требуется указание сложности. - + Doing work necessary to send message. Receiver's required difficulty: %1 and %2 Выполнение работы, требуемой для отправки сообщения. Получатель запросил сложность: %1 и %2 - + Problem: The work demanded by the recipient (%1 and %2) is more difficult than you are willing to do. %3 Проблема: сложность, затребованная получателем (%1 и %2) гораздо больше, чем вы готовы сделать. %3 - + Problem: You are trying to send a message to yourself or a chan but your encryption key could not be found in the keys.dat file. Could not encrypt message. %1 Проблема: вы пытаетесь отправить сообщение самому себе или в чан, но ваш ключ шифрования не найден в файле ключей keys.dat. Невозможно зашифровать сообщение. %1 @@ -1288,9 +1288,9 @@ Receiver's required difficulty: %1 and %2 Выполнение работы, требуемой для отправки сообщения. - + Message sent. Waiting for acknowledgement. Sent on %1 - Сообщение отправлено. Ожидаем подтверждения. Отправлено на %1 + Отправлено. Ожидаем подтверждения. Отправлено в %1 @@ -1298,12 +1298,12 @@ Receiver's required difficulty: %1 and %2 Выполнение работы, требуемой для запроса ключа шифрования. - + Broadcasting the public key request. This program will auto-retry if they are offline. Рассылка запросов открытого ключа шифрования. Программа будет повторять попытки, если они оффлайн. - + Sending public key request. Waiting for reply. Requested at %1 Отправка запроса открытого ключа шифрования. Ожидание ответа. Запрошено в %1 @@ -1323,7 +1323,7 @@ Receiver's required difficulty: %1 and %2 Отметить все сообщения как прочтенные - + Are you sure you would like to mark all messages read? Вы уверены, что хотите отметить все сообщения как прочтенные? @@ -1333,37 +1333,37 @@ Receiver's required difficulty: %1 and %2 Выполнение работы, требуемой для отправки рассылки. - + Proof of work pending Ожидается доказательство работы - + %n object(s) pending proof of work %n объект в ожидании доказательства работы%n объекта в ожидании доказательства работы%n объектов в ожидании доказательства работы%n объектов в ожидании доказательства работы - + %n object(s) waiting to be distributed %n объект ожидает раздачи%n объекта ожидают раздачи%n объектов ожидают раздачи%n объектов ожидают раздачи - + Wait until these tasks finish? Подождать завершения этих задач? - + Problem communicating with proxy: %1. Please check your network settings. Проблема коммуникации с прокси: %1. Пожалуйста, проверьте ваши сетевые настройки. - + SOCKS5 Authentication problem: %1. Please check your SOCKS5 settings. Проблема аутентификации SOCKS5: %1. Пожалуйста, проверьте настройки SOCKS5. - + The time on your computer, %1, may be wrong. Please verify your settings. Время на компьютере, %1, возможно неправильное. Пожалуйста, проверьте ваши настройки. @@ -1432,77 +1432,77 @@ Receiver's required difficulty: %1 and %2 не рекомендовано для чанов - + Problems connecting? Try enabling UPnP in the Network Settings Проблемы подключения? Попробуйте включить UPnP в сетевых настройках. - + Error: Bitmessage addresses start with BM- Please check the recipient address %1 Ошибка: адреса Bitmessage начинаются с "BM-". Пожалуйста, проверьте адрес получателя %1. - + Error: The recipient address %1 is not typed or copied correctly. Please check it. Ошибка: адрес получателя %1 набран или скопирован неправильно. Пожалуйста, проверьте его. - + Error: The recipient address %1 contains invalid characters. Please check it. Ошибка: адрес получателя %1 содержит недопустимые символы. Пожалуйста, проверьте его. - + Error: The version of the recipient address %1 is too high. Either you need to upgrade your Bitmessage software or your acquaintance is being clever. Ошибка: версия адреса получателя %1 слишком высокая. Либо вам нужно обновить программу Bitmessage, либо ваш знакомый - умник. - + Error: Some data encoded in the recipient address %1 is too short. There might be something wrong with the software of your acquaintance. Ошибка: часть данных, закодированных в адресе получателя %1 слишком короткая. Видимо, что-то не так с программой, используемой вашим знакомым. - + Error: Some data encoded in the recipient address %1 is too long. There might be something wrong with the software of your acquaintance. Ошибка: часть данных, закодированных в адресе получателя %1 слишком длинная. Видимо, что-то не так с программой, используемой вашим знакомым. - + Error: Some data encoded in the recipient address %1 is malformed. There might be something wrong with the software of your acquaintance. Ошибка: часть данных, закодированных в адресе получателя %1 сформирована неправильно. Видимо, что-то не так с программой, используемой вашим знакомым. - + Error: Something is wrong with the recipient address %1. Ошибка: что-то не так с адресом получателя %1. - + Synchronisation pending Ожидается синхронизация - + Bitmessage hasn't synchronised with the network, %n object(s) to be downloaded. If you quit now, it may cause delivery delays. Wait until the synchronisation finishes? Bitmessage не синхронизирован с сетью, незагруженных объектов: %n. Выход сейчас может привести к задержкам доставки. Подождать завершения синхронизации?Bitmessage не синхронизирован с сетью, незагруженных объектов: %n. Выход сейчас может привести к задержкам доставки. Подождать завершения синхронизации?Bitmessage не синхронизирован с сетью, незагруженных объектов: %n. Выход сейчас может привести к задержкам доставки. Подождать завершения синхронизации?Bitmessage не синхронизирован с сетью, незагруженных объектов: %n. Выход сейчас может привести к задержкам доставки. Подождать завершения синхронизации? - + Not connected Не подключено - + Bitmessage isn't connected to the network. If you quit now, it may cause delivery delays. Wait until connected and the synchronisation finishes? Bitmessage не подключен к сети. Выход сейчас может привести к задержкам доставки. Подождать подключения и завершения синхронизации? - + Waiting for network connection... Ожидание сетевого подключения... - + Waiting for finishing synchronisation... Ожидание окончания синхронизации... @@ -1533,14 +1533,14 @@ Receiver's required difficulty: %1 and %2 MsgDecode - + The message has an unknown encoding. Perhaps you should upgrade Bitmessage. Сообщение в неизвестной кодировке. Возможно, вам следует обновить Bitmessage. - + Unknown encoding Неизвестная кодировка @@ -1848,77 +1848,77 @@ The 'Random Number' option is selected by default but deterministic ad Всего соединений: - + Since startup: С начала работы: - + Processed 0 person-to-person messages. Обработано 0 сообщений. - + Processed 0 public keys. Обработано 0 открытых ключей. - + Processed 0 broadcasts. Обработано 0 рассылок. - + Inventory lookups per second: 0 Поисков в каталоге в секунду: 0 - + Objects to be synced: Несинхронизированные объекты: - + Stream # № потока Connections - Соединений + - + Since startup on %1 С начала работы в %1 - + Down: %1/s Total: %2 Загрузка: %1/s Всего: %2 - + Up: %1/s Total: %2 Отправка: %1/s Всего: %2 - + Total Connections: %1 Всего соединений: %1 - + Inventory lookups per second: %1 Поисков в каталоге в секунду: %1 - + Up: 0 kB/s Отправка: 0 кБ/с - + Down: 0 kB/s Загрузка: 0 кБ/с @@ -1928,30 +1928,45 @@ The 'Random Number' option is selected by default but deterministic ad Состояние сети - + byte(s) байтбайтбайтбайт - + Object(s) to be synced: %n Несинхронизированные объекты: %nНесинхронизированные объекты: %nНесинхронизированные объекты: %nНесинхронизированные объекты: %n - + Processed %n person-to-person message(s). Обработано %n сообщение.Обработано %n сообщений.Обработано %n сообщений.Обработано %n сообщений. - + Processed %n broadcast message(s). Обработана %n рассылка.Обработано %n рассылок.Обработано %n рассылок.Обработано %n рассылок. - + Processed %n public key(s). Обработан %n открытый ключ.Обработано %n открытых ключей.Обработано %n открытых ключей.Обработано %n открытых ключей. + + + Peer + Узел + + + + User agent + Название приложения + + + + TLS + TLS + newChanDialog From 27f5de0f9cba114004ef9aa49a541dbc07b9a94c Mon Sep 17 00:00:00 2001 From: Peter Surda Date: Wed, 5 Jul 2017 08:52:16 +0200 Subject: [PATCH 128/407] Unified random number provider - not used yet, just an inactive helper function - I received feedback that OpenSSL.rand isn't more secure than os.urandom. I read several debates/analyses about it and concur --- src/helper_random.py | 9 +++++++++ 1 file changed, 9 insertions(+) create mode 100644 src/helper_random.py diff --git a/src/helper_random.py b/src/helper_random.py new file mode 100644 index 00000000..a0fb08f1 --- /dev/null +++ b/src/helper_random.py @@ -0,0 +1,9 @@ +import os + +from pyelliptic.openssl import OpenSSL + +def randomBytes(n): + try: + return os.urandom(n) + except NotImplementedError: + return OpenSSL.rand(n) From e00a02206b875c5aa8b3cb09e8ea2bf735ef7913 Mon Sep 17 00:00:00 2001 From: Peter Surda Date: Wed, 5 Jul 2017 08:57:44 +0200 Subject: [PATCH 129/407] AddrThread - this thread is for spreading new/updated addresses in active connections, analogous to the InvThread - it doesn't do anything yet, this is just a dummy queue at the moment --- src/bitmessagemain.py | 4 ++++ src/network/addrthread.py | 30 ++++++++++++++++++++++++++++++ src/state.py | 1 + 3 files changed, 35 insertions(+) create mode 100644 src/network/addrthread.py diff --git a/src/bitmessagemain.py b/src/bitmessagemain.py index a2d951ed..461486f7 100755 --- a/src/bitmessagemain.py +++ b/src/bitmessagemain.py @@ -57,6 +57,7 @@ from network.networkthread import BMNetworkThread from network.receivequeuethread import ReceiveQueueThread from network.announcethread import AnnounceThread from network.invthread import InvThread +from network.addrthread import AddrThread from network.downloadthread import DownloadThread # Helper Functions @@ -276,6 +277,9 @@ class Main: downloadThread = DownloadThread() downloadThread.daemon = True downloadThread.start() + state.addrThread = AddrThread() + state.addrThread.daemon = True + state.addrThread.start() connectToStream(1) diff --git a/src/network/addrthread.py b/src/network/addrthread.py new file mode 100644 index 00000000..a6c401ab --- /dev/null +++ b/src/network/addrthread.py @@ -0,0 +1,30 @@ +import Queue +import threading + +import addresses +from helper_threading import StoppableThread +from network.connectionpool import BMConnectionPool +from queues import addrQueue +import protocol +import state + +class AddrThread(threading.Thread, StoppableThread): + def __init__(self): + threading.Thread.__init__(self, name="AddrThread") + self.initStop() + self.name = "AddrThread" + + def run(self): + while not state.shutdown: + chunk = [] + while True: + try: + data = addrQueue.get(False) + chunk.append((data[0], data[1])) + except Queue.Empty: + break + + #finish + + addrQueue.iterate() + self.stop.wait(1) diff --git a/src/state.py b/src/state.py index 618b6c92..1b12831e 100644 --- a/src/state.py +++ b/src/state.py @@ -24,6 +24,7 @@ sqlReady = False # set to true by sqlTread when ready for processing maximumNumberOfHalfOpenConnections = 0 invThread = None +addrThread = None ownAddresses = {} From 773d91bbe27b8b5eeebaedfe62a302eac2ef0535 Mon Sep 17 00:00:00 2001 From: Peter Surda Date: Wed, 5 Jul 2017 09:01:40 +0200 Subject: [PATCH 130/407] Unknown object log entry less severe - unnecessarily classified as critical - fixes #1023 --- src/class_objectProcessor.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/class_objectProcessor.py b/src/class_objectProcessor.py index d146dfd2..5d289fcc 100644 --- a/src/class_objectProcessor.py +++ b/src/class_objectProcessor.py @@ -69,7 +69,10 @@ class objectProcessor(threading.Thread): elif objectType == 'checkShutdownVariable': # is more of a command, not an object type. Is used to get this thread past the queue.get() so that it will check the shutdown variable. pass else: - logger.critical('Error! Bug! The class_objectProcessor was passed an object type it doesn\'t recognize: %s' % str(objectType)) + if isinstance(objectType, int): + logger.info('Don\'t know how to handle object type 0x%08X', objectType) + else: + logger.info('Don\'t know how to handle object type %s', objectType) except helper_msgcoding.DecompressionSizeException as e: logger.error("The object is too big after decompression (stopped decompressing at %ib, your configured limit %ib). Ignoring", e.size, BMConfigParser().safeGetInt("zlib", "maxsize")) except varintDecodeError as e: From 9d09f9f3cee36f87f88c68d96d69f61669398a1e Mon Sep 17 00:00:00 2001 From: Peter Surda Date: Wed, 5 Jul 2017 09:07:00 +0200 Subject: [PATCH 131/407] Reduce severity of socks connectivity errors - Fixes #1024 - Fixes #1019 --- src/network/socks4a.py | 6 ++++++ src/network/socks5.py | 6 ++++++ 2 files changed, 12 insertions(+) diff --git a/src/network/socks4a.py b/src/network/socks4a.py index 61f3ec7d..350e163e 100644 --- a/src/network/socks4a.py +++ b/src/network/socks4a.py @@ -84,6 +84,12 @@ class Socks4aConnection(Socks4a): self.writeQueue.put(self.destination[0] + chr(0x00).encode()) self.set_state("pre_connect", 0) + def state_pre_connect(self): + try: + Socks4a.state_pre_connect(self) + except Socks4aError as e: + self.handle_close(e.message) + class Socks4aResolver(Socks4a): def __init__(self, host): diff --git a/src/network/socks5.py b/src/network/socks5.py index 89c3c9b2..f2bc83e4 100644 --- a/src/network/socks5.py +++ b/src/network/socks5.py @@ -156,6 +156,12 @@ class Socks5Connection(Socks5): self.writeQueue.put(struct.pack(">H", self.destination[1])) self.set_state("pre_connect", 0) + def state_pre_connect(self): + try: + Socks5.state_pre_connect(self) + except Socks5Error as e: + self.handle_close(e.message) + class Socks5Resolver(Socks5): def __init__(self, host): From a0bbd21efcd95a7bff9984f52a7cf0e98d8ecea8 Mon Sep 17 00:00:00 2001 From: Peter Surda Date: Wed, 5 Jul 2017 09:17:01 +0200 Subject: [PATCH 132/407] Add ratings to peers - outbound peers now have a rating - it's also shown in the network status tab - currently it's between -1 to +1, changes by 0.1 steps and uses a hyperbolic function 0.05/(1.0 - rating) to convert rating to probability with which we should connect to that node when randomly chosen - it increases when we successfully establish a full outbound connection to a node, and decreases when we fail to do that - onion nodes have priority when using SOCKS --- src/bitmessageqt/networkstatus.py | 15 ++++++++-- src/bitmessageqt/networkstatus.ui | 5 ++++ src/class_singleCleaner.py | 9 ++++-- src/defaultKnownNodes.py | 18 ++++++------ src/helper_bootstrap.py | 46 +++++++++++++++---------------- src/knownnodes.py | 22 ++++++++++++++- src/network/bmproto.py | 43 +++++++++++++++++------------ src/network/connectionchooser.py | 19 ++++++++++++- src/network/connectionpool.py | 5 +++- src/network/tcp.py | 34 +++++++++++++---------- 10 files changed, 143 insertions(+), 73 deletions(-) diff --git a/src/bitmessageqt/networkstatus.py b/src/bitmessageqt/networkstatus.py index 4ede68f7..2609a3c4 100644 --- a/src/bitmessageqt/networkstatus.py +++ b/src/bitmessageqt/networkstatus.py @@ -4,6 +4,7 @@ import shared from tr import _translate from inventory import Inventory, PendingDownloadQueue, PendingUpload +import knownnodes import l10n import network.stats from retranslateui import RetranslateMixin @@ -87,15 +88,23 @@ class NetworkStatus(QtGui.QWidget, RetranslateMixin): self.tableWidgetConnectionCount.setItem(0, 0, QtGui.QTableWidgetItem("%s:%i" % (i.destination.host, i.destination.port)) ) - self.tableWidgetConnectionCount.setItem(0, 1, + self.tableWidgetConnectionCount.setItem(0, 2, QtGui.QTableWidgetItem("%s" % (i.userAgent)) ) - self.tableWidgetConnectionCount.setItem(0, 2, + self.tableWidgetConnectionCount.setItem(0, 3, QtGui.QTableWidgetItem("%s" % (i.tlsVersion)) ) - self.tableWidgetConnectionCount.setItem(0, 3, + self.tableWidgetConnectionCount.setItem(0, 4, QtGui.QTableWidgetItem("%s" % (",".join(map(str,i.streams)))) ) + try: + # FIXME hard coded stream no + rating = knownnodes.knownNodes[1][i.destination]['rating'] + except KeyError: + rating = "-" + self.tableWidgetConnectionCount.setItem(0, 1, + QtGui.QTableWidgetItem("%s" % (rating)) + ) if i.isOutbound: brush = QtGui.QBrush(QtGui.QColor("yellow"), QtCore.Qt.SolidPattern) else: diff --git a/src/bitmessageqt/networkstatus.ui b/src/bitmessageqt/networkstatus.ui index 993cf1c6..f9cc57c4 100644 --- a/src/bitmessageqt/networkstatus.ui +++ b/src/bitmessageqt/networkstatus.ui @@ -117,6 +117,11 @@ Peer + + + Rating + + User agent diff --git a/src/class_singleCleaner.py b/src/class_singleCleaner.py index e6d98989..c21d8cb1 100644 --- a/src/class_singleCleaner.py +++ b/src/class_singleCleaner.py @@ -98,9 +98,12 @@ class singleCleaner(threading.Thread, StoppableThread): with knownnodes.knownNodesLock: for stream in knownnodes.knownNodes: for node in knownnodes.knownNodes[stream].keys(): - if now - knownnodes.knownNodes[stream][node] > 2419200: # 28 days - shared.needToWriteKownNodesToDisk = True - del knownnodes.knownNodes[stream][node] + try: + if now - knownnodes.knownNodes[stream][node]["lastseen"] > 2419200: # 28 days + shared.needToWriteKownNodesToDisk = True + del knownnodes.knownNodes[stream][node] + except TypeError: + print "Error in %s" % (str(node)) # Let us write out the knowNodes to disk if there is anything new to write out. if shared.needToWriteKnownNodesToDisk: diff --git a/src/defaultKnownNodes.py b/src/defaultKnownNodes.py index 5559f1a5..05b65014 100644 --- a/src/defaultKnownNodes.py +++ b/src/defaultKnownNodes.py @@ -12,15 +12,15 @@ def createDefaultKnownNodes(appdata): stream1 = {} #stream1[state.Peer('2604:2000:1380:9f:82e:148b:2746:d0c7', 8080)] = int(time.time()) - stream1[state.Peer('5.45.99.75', 8444)] = int(time.time()) - stream1[state.Peer('75.167.159.54', 8444)] = int(time.time()) - stream1[state.Peer('95.165.168.168', 8444)] = int(time.time()) - stream1[state.Peer('85.180.139.241', 8444)] = int(time.time()) - stream1[state.Peer('158.222.217.190', 8080)] = int(time.time()) - stream1[state.Peer('178.62.12.187', 8448)] = int(time.time()) - stream1[state.Peer('24.188.198.204', 8111)] = int(time.time()) - stream1[state.Peer('109.147.204.113', 1195)] = int(time.time()) - stream1[state.Peer('178.11.46.221', 8444)] = int(time.time()) + stream1[state.Peer('5.45.99.75', 8444)] = {"lastseen": int(time.time()), "rating": 0, "self": False} + stream1[state.Peer('75.167.159.54', 8444)] = {"lastseen": int(time.time()), "rating": 0, "self": False} + stream1[state.Peer('95.165.168.168', 8444)] = {"lastseen": int(time.time()), "rating": 0, "self": False} + stream1[state.Peer('85.180.139.241', 8444)] = {"lastseen": int(time.time()), "rating": 0, "self": False} + stream1[state.Peer('158.222.217.190', 8080)] = {"lastseen": int(time.time()), "rating": 0, "self": False} + stream1[state.Peer('178.62.12.187', 8448)] = {"lastseen": int(time.time()), "rating": 0, "self": False} + stream1[state.Peer('24.188.198.204', 8111)] = {"lastseen": int(time.time()), "rating": 0, "self": False} + stream1[state.Peer('109.147.204.113', 1195)] = {"lastseen": int(time.time()), "rating": 0, "self": False} + stream1[state.Peer('178.11.46.221', 8444)] = {"lastseen": int(time.time()), "rating": 0, "self": False} ############# Stream 2 ################# stream2 = {} diff --git a/src/helper_bootstrap.py b/src/helper_bootstrap.py index 33a533ca..7c7456e1 100644 --- a/src/helper_bootstrap.py +++ b/src/helper_bootstrap.py @@ -9,30 +9,31 @@ import knownnodes import socks import state +def addKnownNode(stream, peer, lastseen=None, self=False): + if lastseen is None: + lastseen = time.time() + knownnodes.knownNodes[stream][peer] = { + "lastseen": lastseen, + "rating": 0, + "self": self, + } + def knownNodes(): try: - # We shouldn't have to use the knownnodes.knownNodesLock because this had - # better be the only thread accessing knownNodes right now. - pickleFile = open(state.appdata + 'knownnodes.dat', 'rb') - loadedKnownNodes = pickle.load(pickleFile) - pickleFile.close() - # The old format of storing knownNodes was as a 'host: (port, time)' - # mapping. The new format is as 'Peer: time' pairs. If we loaded - # data in the old format, transform it to the new style. - for stream, nodes in loadedKnownNodes.items(): - knownnodes.knownNodes[stream] = {} - for node_tuple in nodes.items(): - try: - host, (port, lastseen) = node_tuple - peer = state.Peer(host, port) - except: - peer, lastseen = node_tuple - knownnodes.knownNodes[stream][peer] = lastseen + with open(state.appdata + 'knownnodes.dat', 'rb') as pickleFile: + with knownnodes.knownNodesLock: + knownnodes.knownNodes = pickle.load(pickleFile) + # the old format was {Peer:lastseen, ...} + # the new format is {Peer:{"lastseen":i, "rating":f}} + for stream in knownnodes.knownNodes.keys(): + for node, params in knownnodes.knownNodes[stream].items(): + if isinstance(params, (float, int)): + addKnownNode(stream, node, params) except: knownnodes.knownNodes = defaultKnownNodes.createDefaultKnownNodes(state.appdata) # your own onion address, if setup if BMConfigParser().has_option('bitmessagesettings', 'onionhostname') and ".onion" in BMConfigParser().get('bitmessagesettings', 'onionhostname'): - knownnodes.knownNodes[1][state.Peer(BMConfigParser().get('bitmessagesettings', 'onionhostname'), BMConfigParser().getint('bitmessagesettings', 'onionport'))] = int(time.time()) + addKnownNode(1, state.Peer(BMConfigParser().get('bitmessagesettings', 'onionhostname'), BMConfigParser().getint('bitmessagesettings', 'onionport')), self=True) if BMConfigParser().getint('bitmessagesettings', 'settingsversion') > 10: logger.error('Bitmessage cannot read future versions of the keys file (keys.dat). Run the newer version of Bitmessage.') raise SystemExit @@ -47,17 +48,17 @@ def dns(): try: for item in socket.getaddrinfo('bootstrap8080.bitmessage.org', 80): logger.info('Adding ' + item[4][0] + ' to knownNodes based on DNS bootstrap method') - knownnodes.knownNodes[1][state.Peer(item[4][0], 8080)] = int(time.time()) + addKnownNode(1, state.Peer(item[4][0], 8080)) except: logger.error('bootstrap8080.bitmessage.org DNS bootstrapping failed.') try: for item in socket.getaddrinfo('bootstrap8444.bitmessage.org', 80): logger.info ('Adding ' + item[4][0] + ' to knownNodes based on DNS bootstrap method') - knownnodes.knownNodes[1][state.Peer(item[4][0], 8444)] = int(time.time()) + addKnownNode(1, state.Peer(item[4][0], 8444)) except: logger.error('bootstrap8444.bitmessage.org DNS bootstrapping failed.') elif BMConfigParser().get('bitmessagesettings', 'socksproxytype') == 'SOCKS5': - knownnodes.knownNodes[1][state.Peer('quzwelsuziwqgpt2.onion', 8444)] = int(time.time()) + addKnownNode(1, state.Peer('quzwelsuziwqgpt2.onion', 8444)) logger.debug("Adding quzwelsuziwqgpt2.onion:8444 to knownNodes.") for port in [8080, 8444]: logger.debug("Resolving %i through SOCKS...", port) @@ -90,7 +91,6 @@ def dns(): else: if ip is not None: logger.info ('Adding ' + ip + ' to knownNodes based on SOCKS DNS bootstrap method') - knownnodes.knownNodes[1][state.Peer(ip, port)] = time.time() + addKnownNode(1, state.Peer(ip, port)) else: logger.info('DNS bootstrap skipped because the proxy type does not support DNS resolution.') - diff --git a/src/knownnodes.py b/src/knownnodes.py index 8f566d43..86d39cbe 100644 --- a/src/knownnodes.py +++ b/src/knownnodes.py @@ -16,10 +16,30 @@ def saveKnownNodes(dirName = None): with open(dirName + 'knownnodes.dat', 'wb') as output: pickle.dump(knownNodes, output) +def increaseRating(peer): + increaseAmount = 0.1 + maxRating = 1 + with knownNodesLock: + for stream in knownNodes.keys(): + try: + knownNodes[stream][peer]["rating"] = min(knownNodes[stream][peer]["rating"] + increaseAmount, maxRating) + except KeyError: + pass + +def decreaseRating(peer): + decreaseAmount = 0.1 + minRating = -1 + with knownNodesLock: + for stream in knownNodes.keys(): + try: + knownNodes[stream][peer]["rating"] = max(knownNodes[stream][peer]["rating"] - decreaseAmount, minRating) + except KeyError: + pass + def trimKnownNodes(recAddrStream = 1): if len(knownNodes[recAddrStream]) < BMConfigParser().get("knownnodes", "maxnodes"): return with knownNodesLock: - oldestList = sorted(knownNodes[recAddrStream], key=knownNodes[recAddrStream].get)[:knownNodesTrimAmount] + oldestList = sorted(knownNodes[recAddrStream], key=lambda x: x['lastseen'])[:knownNodesTrimAmount] for oldest in oldestList: del knownNodes[recAddrStream][oldest] diff --git a/src/network/bmproto.py b/src/network/bmproto.py index 0c70f12e..c45f9ca3 100644 --- a/src/network/bmproto.py +++ b/src/network/bmproto.py @@ -328,16 +328,21 @@ class BMProto(AdvancedDispatcher, ObjectTracker): decodedIP = protocol.checkIPAddress(ip) if stream not in state.streamsInWhichIAmParticipating: continue - #print "maybe adding %s in stream %i to knownnodes (%i)" % (decodedIP, stream, len(knownnodes.knownNodes[stream])) if decodedIP is not False and seenTime > time.time() - BMProto.addressAlive: peer = state.Peer(decodedIP, port) - if peer in knownnodes.knownNodes[stream] and knownnodes.knownNodes[stream][peer] > seenTime: + if peer in knownnodes.knownNodes[stream] and knownnodes.knownNodes[stream][peer]["lastseen"] > seenTime: continue if len(knownnodes.knownNodes[stream]) < BMConfigParser().get("knownnodes", "maxnodes"): with knownnodes.knownNodesLock: - knownnodes.knownNodes[stream][peer] = seenTime - #knownnodes.knownNodes[stream][peer] = seenTime - #AddrUploadQueue().put((stream, peer)) + try: + knownnodes.knownNodes[stream][peer]["lastseen"] = seenTime + except (TypeError, KeyError): + knownnodes.knownNodes[stream][peer] = { + "lastseen": seenTime, + "rating": 0, + "self": False, + } + addrQueue.put((stream, peer, self)) return True def bm_command_portcheck(self): @@ -449,18 +454,22 @@ class BMProto(AdvancedDispatcher, ObjectTracker): def assembleAddr(peerList): if isinstance(peerList, state.Peer): peerList = (peerList) - # TODO handle max length, now it's done by upper layers - payload = addresses.encodeVarint(len(peerList)) - for address in peerList: - stream, peer, timestamp = address - payload += struct.pack( - '>Q', timestamp) # 64-bit time - payload += struct.pack('>I', stream) - payload += struct.pack( - '>q', 1) # service bit flags offered by this node - payload += protocol.encodeHost(peer.host) - payload += struct.pack('>H', peer.port) # remote port - return protocol.CreatePacket('addr', payload) + if not peerList: + return b'' + retval = b'' + for i in range(0, len(peerList), BMProto.maxAddrCount): + payload = addresses.encodeVarint(len(peerList[i:i + BMProto.maxAddrCount])) + for address in peerList[i:i + BMProto.maxAddrCount]: + stream, peer, timestamp = address + payload += struct.pack( + '>Q', timestamp) # 64-bit time + payload += struct.pack('>I', stream) + payload += struct.pack( + '>q', 1) # service bit flags offered by this node + payload += protocol.encodeHost(peer.host) + payload += struct.pack('>H', peer.port) # remote port + retval += protocol.CreatePacket('addr', payload) + return retval def handle_close(self, reason=None): self.set_state("close") diff --git a/src/network/connectionchooser.py b/src/network/connectionchooser.py index 0770bfa6..2cce5036 100644 --- a/src/network/connectionchooser.py +++ b/src/network/connectionchooser.py @@ -7,6 +7,7 @@ from queues import portCheckerQueue, peerDiscoveryQueue import state def chooseConnection(stream): + haveOnion = BMConfigParser().safeGet("bitmessagesettings", "socksproxytype")[0:5] == 'SOCKS' if state.trustedPeer: return state.trustedPeer try: @@ -17,5 +18,21 @@ def chooseConnection(stream): retval = peerDiscoveryQueue.get(False) peerDiscoveryQueue.task_done() except Queue.Empty: - return random.choice(knownnodes.knownNodes[stream].keys()) + for i in range(50): + peer = random.choice(knownnodes.knownNodes[stream].keys()) + try: + rating = knownnodes.knownNodes[stream][peer]["rating"] + except TypeError: + print "Error in %s" % (peer) + rating = 0 + if haveOnion and peer.host.endswith('.onion') and rating > 0: + rating *= 10 + if rating > 1: + rating = 1 + try: + if 0.05/(1.0-rating) > random.random(): + return peer + except ZeroDivisionError: + return peer + raise ValueError return retval diff --git a/src/network/connectionpool.py b/src/network/connectionpool.py index ae76c318..9328720f 100644 --- a/src/network/connectionpool.py +++ b/src/network/connectionpool.py @@ -135,7 +135,10 @@ class BMConnectionPool(object): pending = len(self.outboundConnections) - established if established < BMConfigParser().safeGetInt("bitmessagesettings", "maxoutboundconnections"): for i in range(state.maximumNumberOfHalfOpenConnections - pending): - chosen = chooseConnection(random.choice(self.streams)) + try: + chosen = chooseConnection(random.choice(self.streams)) + except ValueError: + continue if chosen in self.outboundConnections: continue if chosen.host in self.inboundConnections: diff --git a/src/network/tcp.py b/src/network/tcp.py index bd925063..120cfdce 100644 --- a/src/network/tcp.py +++ b/src/network/tcp.py @@ -98,6 +98,8 @@ class TCPConnection(BMProto, TLSDispatcher): UISignalQueue.put(('updateNetworkStatusTab', 'no data')) self.antiIntersectionDelay(True) self.fullyEstablished = True + if self.isOutbound: + knownnodes.increaseRating(self.destination) self.sendAddr() self.sendBigInv() @@ -117,7 +119,7 @@ class TCPConnection(BMProto, TLSDispatcher): with knownnodes.knownNodesLock: if len(knownnodes.knownNodes[stream]) > 0: filtered = {k: v for k, v in knownnodes.knownNodes[stream].items() - if v > (int(time.time()) - shared.maximumAgeOfNodesThatIAdvertiseToOthers)} + if v["lastseen"] > (int(time.time()) - shared.maximumAgeOfNodesThatIAdvertiseToOthers)} elemCount = len(filtered) if elemCount > maxAddrCount: elemCount = maxAddrCount @@ -126,25 +128,21 @@ class TCPConnection(BMProto, TLSDispatcher): # sent 250 only if the remote isn't interested in it if len(knownnodes.knownNodes[stream * 2]) > 0 and stream not in self.streams: filtered = {k: v for k, v in knownnodes.knownNodes[stream*2].items() - if v > (int(time.time()) - shared.maximumAgeOfNodesThatIAdvertiseToOthers)} + if v["lastseen"] > (int(time.time()) - shared.maximumAgeOfNodesThatIAdvertiseToOthers)} elemCount = len(filtered) if elemCount > maxAddrCount / 2: elemCount = int(maxAddrCount / 2) addrs[stream * 2] = random.sample(filtered.items(), elemCount) if len(knownnodes.knownNodes[(stream * 2) + 1]) > 0 and stream not in self.streams: filtered = {k: v for k, v in knownnodes.knownNodes[stream*2+1].items() - if v > (int(time.time()) - shared.maximumAgeOfNodesThatIAdvertiseToOthers)} + if v["lastseen"] > (int(time.time()) - shared.maximumAgeOfNodesThatIAdvertiseToOthers)} elemCount = len(filtered) if elemCount > maxAddrCount / 2: elemCount = int(maxAddrCount / 2) addrs[stream * 2 + 1] = random.sample(filtered.items(), elemCount) for substream in addrs.keys(): - for peer, timestamp in addrs[substream]: - templist.append((substream, peer, timestamp)) - if len(templist) >= BMProto.maxAddrCount: - self.writeQueue.put(BMProto.assembleAddr(templist)) - templist = [] - # flush + for peer, params in addrs[substream]: + templist.append((substream, peer, params["lastseen"])) if len(templist) > 0: self.writeQueue.put(BMProto.assembleAddr(templist)) @@ -163,16 +161,22 @@ class TCPConnection(BMProto, TLSDispatcher): self.connectedAt = time.time() def handle_read(self): -# try: TLSDispatcher.handle_read(self) -# except socket.error as e: -# logger.debug("%s:%i: Handle read fail: %s" % (self.destination.host, self.destination.port, str(e))) + if self.isOutbound and self.fullyEstablished: + for s in self.streams: + try: + with knownnodes.knownNodesLock: + knownnodes.knownNodes[s][self.destination]["lastseen"] = time.time() + except KeyError: + pass def handle_write(self): -# try: TLSDispatcher.handle_write(self) -# except socket.error as e: -# logger.debug("%s:%i: Handle write fail: %s" % (self.destination.host, self.destination.port, str(e))) + + def handle_close(self, reason=None): + if self.isOutbound and not self.fullyEstablished: + knownnodes.decreaseRating(self.destination) + BMProto.handle_close(self, reason) class Socks5BMConnection(Socks5Connection, TCPConnection): From d086781ce82be6e19d8b7706a21d463afe4d6073 Mon Sep 17 00:00:00 2001 From: Peter Surda Date: Wed, 5 Jul 2017 09:19:18 +0200 Subject: [PATCH 133/407] AddrQueue fix - forgot to commit import --- src/network/bmproto.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/network/bmproto.py b/src/network/bmproto.py index c45f9ca3..9ccd69ca 100644 --- a/src/network/bmproto.py +++ b/src/network/bmproto.py @@ -21,7 +21,7 @@ from network.proxy import Proxy, ProxyError, GeneralProxyError import addresses from bmconfigparser import BMConfigParser -from queues import objectProcessorQueue, portCheckerQueue, invQueue +from queues import objectProcessorQueue, portCheckerQueue, invQueue, addrQueue import shared import state import protocol From 846fced0a21a15f9f7d98e610b173edb07922b9d Mon Sep 17 00:00:00 2001 From: Peter Surda Date: Wed, 5 Jul 2017 09:19:56 +0200 Subject: [PATCH 134/407] Remove obsolete inactive code --- src/network/advanceddispatcher.py | 5 ----- 1 file changed, 5 deletions(-) diff --git a/src/network/advanceddispatcher.py b/src/network/advanceddispatcher.py index 1abc7975..0945d764 100644 --- a/src/network/advanceddispatcher.py +++ b/src/network/advanceddispatcher.py @@ -41,11 +41,6 @@ class AdvancedDispatcher(asyncore.dispatcher): if not self.connected: return maxLoop = 20 -# try: -# sys._getframe(200) -# logger.error("Stack depth warning") -# except ValueError: -# pass while maxLoop > 0: try: if getattr(self, "state_" + str(self.state))() is False: From fc19e4119ad25e1436f71f63e64bab5e66dfeeb9 Mon Sep 17 00:00:00 2001 From: Peter Surda Date: Wed, 5 Jul 2017 09:25:49 +0200 Subject: [PATCH 135/407] Download thread updates - now tracks downloads globally too, so it doesn't request the same object from multiple peers at the same time - retries at the earliest every minute - stops trying to download an object after an hour - minor fixes in retrying downloading invalid objects --- src/bitmessagemain.py | 6 ++-- src/network/bmproto.py | 53 ++++++++++++++----------------- src/network/connectionpool.py | 2 +- src/network/downloadthread.py | 29 +++++++++++++---- src/network/objectracker.py | 5 +-- src/network/receivequeuethread.py | 2 +- src/state.py | 1 + 7 files changed, 55 insertions(+), 43 deletions(-) diff --git a/src/bitmessagemain.py b/src/bitmessagemain.py index 461486f7..88fa3c1d 100755 --- a/src/bitmessagemain.py +++ b/src/bitmessagemain.py @@ -274,12 +274,12 @@ class Main: state.invThread = InvThread() state.invThread.daemon = True state.invThread.start() - downloadThread = DownloadThread() - downloadThread.daemon = True - downloadThread.start() state.addrThread = AddrThread() state.addrThread.daemon = True state.addrThread.start() + state.downloadThread = DownloadThread() + state.downloadThread.daemon = True + state.downloadThread.start() connectToStream(1) diff --git a/src/network/bmproto.py b/src/network/bmproto.py index 9ccd69ca..b08b6d61 100644 --- a/src/network/bmproto.py +++ b/src/network/bmproto.py @@ -275,47 +275,24 @@ class BMProto(AdvancedDispatcher, ObjectTracker): self.object.checkEOLSanity() self.object.checkAlreadyHave() except (BMObjectExpiredError, BMObjectAlreadyHaveError) as e: - for connection in network.connectionpool.BMConnectionPool().inboundConnections.values() + \ - network.connectionpool.BMConnectionPool().outboundConnections.values(): - try: - with connection.objectsNewToThemLock: - del connection.objectsNewToThem[self.object.inventoryHash] - except KeyError: - pass - try: - with connection.objectsNewToMeLock: - del connection.objectsNewToMe[self.object.inventoryHash] - except KeyError: - pass + BMProto.stopDownloadingObject(self.object.inventoryHash) raise e try: self.object.checkStream() except (BMObjectUnwantedStreamError,) as e: - for connection in network.connectionpool.BMConnectionPool().inboundConnections.values() + \ - network.connectionpool.BMConnectionPool().outboundConnections.values(): - try: - with connection.objectsNewToMeLock: - del connection.objectsNewToMe[self.object.inventoryHash] - except KeyError: - pass - if not BMConfigParser().get("inventory", "acceptmismatch"): - try: - with connection.objectsNewToThemLock: - del connection.objectsNewToThem[self.object.inventoryHash] - except KeyError: - pass + BMProto.stopDownloadingObject(self.object.inventoryHash, BMConfigParser().get("inventory", "acceptmismatch")) if not BMConfigParser().get("inventory", "acceptmismatch"): raise e - self.object.checkObjectByType() + try: + self.object.checkObjectByType() + objectProcessorQueue.put((self.object.objectType, self.object.data)) + except BMObjectInvalidError as e: + BMProto.stopDownloadingObject(self.object.inventoryHash, True) Inventory()[self.object.inventoryHash] = ( self.object.objectType, self.object.streamNumber, self.payload[objectOffset:], self.object.expiresTime, self.object.tag) - objectProcessorQueue.put((self.object.objectType,self.object.data)) - #DownloadQueue().task_done(self.object.inventoryHash) invQueue.put((self.object.streamNumber, self.object.inventoryHash, self)) - #ObjUploadQueue().put(UploadElem(self.object.streamNumber, self.object.inventoryHash)) - #broadcastToSendDataQueues((streamNumber, 'advertiseobject', inventoryHash)) return True def _decode_addr(self): @@ -471,6 +448,22 @@ class BMProto(AdvancedDispatcher, ObjectTracker): retval += protocol.CreatePacket('addr', payload) return retval + @staticmethod + def stopDownloadingObject(hashId, forwardAnyway=False): + for connection in network.connectionpool.BMConnectionPool().inboundConnections.values() + \ + network.connectionpool.BMConnectionPool().outboundConnections.values(): + try: + with connection.objectsNewToMeLock: + del connection.objectsNewToMe[hashId] + except KeyError: + pass + if not forwardAnyway: + try: + with connection.objectsNewToThemLock: + del connection.objectsNewToThem[hashId] + except KeyError: + pass + def handle_close(self, reason=None): self.set_state("close") if reason is None: diff --git a/src/network/connectionpool.py b/src/network/connectionpool.py index 9328720f..c4aa53d8 100644 --- a/src/network/connectionpool.py +++ b/src/network/connectionpool.py @@ -44,7 +44,7 @@ class BMConnectionPool(object): del i.objectsNewToMe[hashid] except KeyError: with i.objectsNewToThemLock: - i.objectsNewToThem[hashid] = True + i.objectsNewToThem[hashid] = time.time() if i == connection: try: with i.objectsNewToThemLock: diff --git a/src/network/downloadthread.py b/src/network/downloadthread.py index a8d3e1f7..9c7e92da 100644 --- a/src/network/downloadthread.py +++ b/src/network/downloadthread.py @@ -1,4 +1,5 @@ import threading +import time import addresses #from bmconfigparser import BMConfigParser @@ -9,26 +10,38 @@ from network.connectionpool import BMConnectionPool import protocol class DownloadThread(threading.Thread, StoppableThread): - maxPending = 500 - requestChunk = 1000 + maxPending = 50 + requestChunk = 100 + requestTimeout = 60 + cleanInterval = 60 + requestExpires = 600 def __init__(self): threading.Thread.__init__(self, name="DownloadThread") self.initStop() self.name = "DownloadThread" logger.info("init download thread") + self.pending = {} + self.lastCleaned = time.time() + + def cleanPending(self): + deadline = time.time() - DownloadThread.requestExpires + self.pending = {k: v for k, v in self.pending.iteritems() if v >= deadline} + self.lastCleaned = time.time() def run(self): while not self._stopped: requested = 0 for i in BMConnectionPool().inboundConnections.values() + BMConnectionPool().outboundConnections.values(): - # this may take a while, but it needs a consistency so I think it's better + now = time.time() + timedOut = now - DownloadThread.requestTimeout + # this may take a while, but it needs a consistency so I think it's better to lock a bigger chunk with i.objectsNewToMeLock: - downloadPending = len(list((k for k, v in i.objectsNewToMe.iteritems() if not v))) + downloadPending = len(list((k for k, v in i.objectsNewToMe.iteritems() if k in self.pending and self.pending[k] > timedOut))) if downloadPending >= DownloadThread.maxPending: continue # keys with True values in the dict - request = list((k for k, v in i.objectsNewToMe.iteritems() if v)) + request = list((k for k, v in i.objectsNewToMe.iteritems() if k not in self.pending or self.pending[k] < timedOut)) if not request: continue if len(request) > DownloadThread.requestChunk - downloadPending: @@ -36,9 +49,13 @@ class DownloadThread(threading.Thread, StoppableThread): # mark them as pending for k in request: i.objectsNewToMe[k] = False + self.pending[k] = now payload = addresses.encodeVarint(len(request)) + ''.join(request) i.writeQueue.put(protocol.CreatePacket('getdata', payload)) logger.debug("%s:%i Requesting %i objects", i.destination.host, i.destination.port, len(request)) requested += len(request) - self.stop.wait(1) + if time.time() >= self.lastCleaned + DownloadThread.cleanInterval: + self.cleanPending() + if not requested: + self.stop.wait(1) diff --git a/src/network/objectracker.py b/src/network/objectracker.py index 5c0ad147..a7295c21 100644 --- a/src/network/objectracker.py +++ b/src/network/objectracker.py @@ -28,6 +28,7 @@ class ObjectTracker(object): invCleanPeriod = 300 invInitialCapacity = 50000 invErrorRate = 0.03 + trackingExpires = 3600 def __init__(self): self.objectsNewToMe = {} @@ -62,9 +63,9 @@ class ObjectTracker(object): with self.objectsNewToMeLock: tmp = self.objectsNewToMe.copy() self.objectsNewToMe = tmp + deadline = time.time() - ObjectTracker.trackingExpires with self.objectsNewToThemLock: - tmp = self.objectsNewToThem.copy() - self.objectsNewToThem = tmp + self.objectsNewToThem = {k: v for k, v in self.objectsNewToThem.iteritems() if v >= deadline} self.lastCleaned = time.time() def hasObj(self, hashid): diff --git a/src/network/receivequeuethread.py b/src/network/receivequeuethread.py index 8360ab45..137131c5 100644 --- a/src/network/receivequeuethread.py +++ b/src/network/receivequeuethread.py @@ -70,7 +70,7 @@ class ReceiveQueueThread(threading.Thread, StoppableThread): with connection.objectsNewToThemLock: for objHash in Inventory().unexpired_hashes_by_stream(stream): bigInvList[objHash] = 0 - connection.objectsNewToThem[objHash] = True + connection.objectsNewToThem[objHash] = time.time() objectCount = 0 payload = b'' # Now let us start appending all of these hashes together. They will be diff --git a/src/state.py b/src/state.py index 1b12831e..08da36eb 100644 --- a/src/state.py +++ b/src/state.py @@ -25,6 +25,7 @@ maximumNumberOfHalfOpenConnections = 0 invThread = None addrThread = None +downloadThread = None ownAddresses = {} From fe0664640ea2c37421289e4e88e4ed6d99896ec0 Mon Sep 17 00:00:00 2001 From: Peter Surda Date: Wed, 5 Jul 2017 09:27:52 +0200 Subject: [PATCH 136/407] Migrate antiIntersectionDelay to asyncore - implemented by ignoring getdata during the delay rather than sleeping as it was in the threaded model - it can happen that a valid getdata request is received during the delay. A node should be implemented in a way that retries to download, that may not be the case with older PyBitmessage versions or other implementations --- src/network/bmproto.py | 14 ++++---------- src/network/receivequeuethread.py | 2 +- src/network/tcp.py | 8 ++++---- 3 files changed, 9 insertions(+), 15 deletions(-) diff --git a/src/network/bmproto.py b/src/network/bmproto.py index b08b6d61..abf2f0ec 100644 --- a/src/network/bmproto.py +++ b/src/network/bmproto.py @@ -234,17 +234,11 @@ class BMProto(AdvancedDispatcher, ObjectTracker): def bm_command_getdata(self): items = self.decode_payload_content("L32s") -# if time.time() < self.skipUntil: -# print "skipping getdata" -# return True + # skip? + if time.time() < self.skipUntil: + return True for i in items: - #print "received getdata request for item %s" % (hexlify(i)) - #logger.debug('received getdata request for item:' + hexlify(i)) - #if i in ObjUploadQueue.streamElems(1): - if False: - self.antiIntersectionDelay() - else: - self.receiveQueue.put(("object", i)) + self.receiveQueue.put(("object", i)) return True def bm_command_inv(self): diff --git a/src/network/receivequeuethread.py b/src/network/receivequeuethread.py index 137131c5..442c755a 100644 --- a/src/network/receivequeuethread.py +++ b/src/network/receivequeuethread.py @@ -54,7 +54,7 @@ class ReceiveQueueThread(threading.Thread, StoppableThread): connection.writeQueue.put(protocol.CreatePacket('object', Inventory()[objHash].payload)) except KeyError: connection.antiIntersectionDelay() - logger.warning('%s asked for an object with a getdata which is not in either our memory inventory or our SQL inventory. We probably cleaned it out after advertising it but before they got around to asking for it.' % (connection.destination,)) + logger.info('%s asked for an object we don\'t have.', connection.destination) def command_biginv(self, connection, dummy): def sendChunk(): diff --git a/src/network/tcp.py b/src/network/tcp.py index 120cfdce..c2052df1 100644 --- a/src/network/tcp.py +++ b/src/network/tcp.py @@ -29,7 +29,7 @@ from network.tls import TLSDispatcher import addresses from bmconfigparser import BMConfigParser -from queues import objectProcessorQueue, portCheckerQueue, UISignalQueue +from queues import invQueue, objectProcessorQueue, portCheckerQueue, UISignalQueue import shared import state import protocol @@ -77,7 +77,7 @@ class TCPConnection(BMProto, TLSDispatcher): def antiIntersectionDelay(self, initial = False): # estimated time for a small object to propagate across the whole network - delay = math.ceil(math.log(max(len(knownnodes.knownNodes[x]) for x in knownnodes.knownNodes) + 2, 20)) * (0.2 + UploadQueue.queueCount/2) + delay = math.ceil(math.log(max(len(knownnodes.knownNodes[x]) for x in knownnodes.knownNodes) + 2, 20)) * (0.2 + invQueue.queueCount/2.0) # take the stream with maximum amount of nodes # +2 is to avoid problems with log(0) and log(1) # 20 is avg connected nodes count @@ -86,9 +86,9 @@ class TCPConnection(BMProto, TLSDispatcher): if initial: self.skipUntil = self.connectedAt + delay if self.skipUntil > time.time(): - logger.debug("Skipping processing for %.2fs", self.skipUntil - time.time()) + logger.debug("Initial skipping processing getdata for %.2fs", self.skipUntil - time.time()) else: - logger.debug("Skipping processing due to missing object for %.2fs", self.skipUntil - time.time()) + logger.debug("Skipping processing getdata due to missing object for %.2fs", self.skipUntil - time.time()) self.skipUntil = time.time() + delay def set_connection_fully_established(self): From 27f9fb0237465d2d9580e96fb140913d273916f2 Mon Sep 17 00:00:00 2001 From: PyBitmessage Translations Date: Wed, 5 Jul 2017 17:09:30 +0200 Subject: [PATCH 137/407] Auto-updated language ru from transifex --- src/translations/bitmessage_ru.qm | Bin 91957 -> 92019 bytes src/translations/bitmessage_ru.ts | 45 +++++++++++++++++------------- 2 files changed, 25 insertions(+), 20 deletions(-) diff --git a/src/translations/bitmessage_ru.qm b/src/translations/bitmessage_ru.qm index b0d1d886b8566e42577ef17983b78da1bd80f8cd..f298a70d7e40e8131fe5229ca83b9eb7318c59f5 100644 GIT binary patch delta 897 zcmW+!ZA?>V7=505-+SA8-+ov{D|Ylk6gxE9)!AO>EX}A@tO)B278QOlA&4T=FqVJ` zvJjlpeb_t_6XM5`NgPf^;|R=XWGZM(O0p2Mh;h*@xWoXPnPdrm?R7uioaD_p&y(kz z(>tvEGproW&}INY0MJ;F%33(nIS*j#B7mL%!1mREn`=|0C7E@g^nVHXVFsw(viMAa zWm8Pxbn2>sFH#SDxSkYDVi8rKJEsJawatL5uR&RFK0wFE2L(o{14B~y zZAznoLo|VYETt^TkGE)pfT zfhjiU%w~Xp7qf7mI5vL|ixzhS{87OQ)6W2^8rjFQjsVU$$EtglihBoWkR`Pw`(->& zu!ksD+2{IBX=|+pT+^={xUd$`{+!M!ZfvJPWuX*VP7{h(kIz*%c1;0#|EAfh;hInn zeAWU`zEeFG9|J5iDW;m*Q}w|nu_L;b+Evqa*3xz|0#Gn)*&E<2z}OjTwCtDGIO(Ouk7cyPYT_xXwi?<+E&cL@u;2oHZS}cc>wHh1 z$ojfIYlAT4t>0+Sx-=fm(zjO$QoKvw@h}K5w*G&_`A77Y@+W}iY`yc(7Xazs_4E1D zBGq>NQt}nRPbc)Bx$vm7UmqA2={mllux8*Ps?ZEA%UmF#Z9`nwTDB%1jG^H6ir}@36Nx*eq@X^8!fJM!G ze5Maz`d2=ly#p}hrl4+WH*CAt9Tzh>HR^__QHLJERdiSPX$Nih_l}6Rb&X9=Zf}gA?LFCM!;2fox$5)z?H4e_dTKn zp4-%6GcitSn;WZW%x0=hwSZepI<`FCR%h?4vtzH_aIkC^Wbd&8wt|J(yH;U1sBhj! Um7mtu*BpK1Mt5J12Q!NP1IGRS;Q#;t delta 895 zcmW-fe@xVM7{}kweSh42zu)`ODXG`Rog{dHgX^ffEA;Lr$0ee0F+)(nR>@QvpaX2d zgo=$NMc(zqYz%U3wgrF0`oSohbCtDfJGOSY1`V?x=*Ey%W^NYLXZ`iwp1t?p^E~g@ z%^78CMoHyp761W|XE~j_d5!&7;6M>D832w{fc8C@u1mT?P(C^ZN?w6FE{Wj*nD-`x zUP@o^(wFIZAKlFg)zdm|qV;($)F15zRh&Y@R1^rAvA?4X^jHLq>Yz{?4^zmr?GMh7 zmr{I#{N&|alb_ODZSt6Q+~QVKk_P#LsZS(EBrk2?CMo8+WSX--39|idx+Zr3OBTuc ziX!r6cuMjWZjp-PlfVaV>4EHfKzLaST0aMExGII(dqEF(urQ6@fmfrY5~+hT&;$+Pt^InVvTqK z52<1233LD17*IZA9@y;yRd(@Z^)SUbV)i?$ED!&_44nSPvL{*xN_6r`^MENa%ZkNM z_i@k?rZ#T1c(ooav+^d$)y=~epYvdri5a#3j)Tfm#?w99K@NxUa(6$-K4GLT&H*FWjFD^{wCO`$(Y$%bjoIgx z#iweGxyoUnsM}at;{)z|Yb zCo6DiXPa@D0^DWP)2o~@{N&~pqpdW5(f(U)z6f&LS3P!6?)MIV!99Q$a+J1pfwrA> z1i$it%Bmb&-GU1DYdmHRyK)?-=LBzkA3M%@W`XvMVВсего соединений: - + Since startup: С начала работы: - + Processed 0 person-to-person messages. Обработано 0 сообщений. - + Processed 0 public keys. Обработано 0 открытых ключей. - + Processed 0 broadcasts. Обработано 0 рассылок. - + Inventory lookups per second: 0 Поисков в каталоге в секунду: 0 - + Objects to be synced: Несинхронизированные объекты: - + Stream # № потока @@ -1888,37 +1888,37 @@ The 'Random Number' option is selected by default but deterministic ad - + Since startup on %1 С начала работы в %1 - + Down: %1/s Total: %2 Загрузка: %1/s Всего: %2 - + Up: %1/s Total: %2 Отправка: %1/s Всего: %2 - + Total Connections: %1 Всего соединений: %1 - + Inventory lookups per second: %1 Поисков в каталоге в секунду: %1 - + Up: 0 kB/s Отправка: 0 кБ/с - + Down: 0 kB/s Загрузка: 0 кБ/с @@ -1928,27 +1928,27 @@ The 'Random Number' option is selected by default but deterministic ad Состояние сети - + byte(s) байтбайтбайтбайт - + Object(s) to be synced: %n Несинхронизированные объекты: %nНесинхронизированные объекты: %nНесинхронизированные объекты: %nНесинхронизированные объекты: %n - + Processed %n person-to-person message(s). Обработано %n сообщение.Обработано %n сообщений.Обработано %n сообщений.Обработано %n сообщений. - + Processed %n broadcast message(s). Обработана %n рассылка.Обработано %n рассылок.Обработано %n рассылок.Обработано %n рассылок. - + Processed %n public key(s). Обработан %n открытый ключ.Обработано %n открытых ключей.Обработано %n открытых ключей.Обработано %n открытых ключей. @@ -1959,11 +1959,16 @@ The 'Random Number' option is selected by default but deterministic ad + Rating + Рейтинг + + + User agent Название приложения - + TLS TLS From 1dbf98d7f74c783ec1aeccf1d3825fa2fda4df2e Mon Sep 17 00:00:00 2001 From: PyBitmessage Translations Date: Thu, 6 Jul 2017 00:06:05 +0200 Subject: [PATCH 138/407] Auto-updated language ja from transifex --- src/translations/bitmessage_ja.qm | Bin 65563 -> 65615 bytes src/translations/bitmessage_ja.ts | 49 ++++++++++++++++-------------- 2 files changed, 27 insertions(+), 22 deletions(-) diff --git a/src/translations/bitmessage_ja.qm b/src/translations/bitmessage_ja.qm index d9acc50c19b7d0bde1dd7fc8242ce639528ca365..d5116555b3041a779cd40a5be68969fc0bd41510 100644 GIT binary patch delta 872 zcmXAnaZHqT7{|ZA_ulutd*9!2cnV$E;H?%i+Vp~ToY{`E@MbpU0*9w1D@_J8MU7xX zHjiRBrP{bXFpEfYqZ(}ijhyL5V0P}J9W&ZeOTf@t>YdCun+x1q~KI#yDafXt@ z66K@jbn<{{rPVI32 zdiHXTzSKNiJFcxzep^oST9!HI(l>v)z%<(_UANq0`r&$Zw05Q5_2M*e`y7RIzY*3a zws4JW^VFgjES+-Mn~w*9Ri*Yr8#92}2b9z+g`a$OuY0Fs_3fL$u5QQX!ZL>X3)R^V zDKqJG-|l5fD;)*OP#RS@EF)+n?;iuse?ad$RxBOPHRXPGj?c_xo7lk*g5nPEhCZ`u z7YEHXm~Tw^fKO8X*W{vkaQk)Oa@6dcy$7_ZW^dtL;P+W`AhHNVub8JLQ`&vO93JIa zew$ALBTKm=sNArOuVl*GybkB*sKp3wtChuPIHf)&OCB)bYgO{~#^*R?Oul=dnRT~p z8ye@Y?#a%JMZoCuvdefHc=cGy}+As`SXXxz)zO^wSfuzahx(uk2p&{ z({BWv`!;+GT%D#m)8YYin1#x08eKATjGNBOjTd?LZqj6>_?z~~h}cST=@pk~LDuAt zIbF9(p5P@3xfZir3}zwKo4p*+FQn$z)B%(GQ+YTtXMO~HtDp@mIsG)e~X0t&s z&B%T+X+8*s2`-T>!5DREh?`3#N;Y-o;RfLkN*Wjnq5+L#bJ=3uZnD2la__m{J?DJq zR=f0NyVSKxH3KLFem)t?aoaxt_HG0sGeG?|;P+r`SIChI`MXbmrD`ZcB0p?_sXD-M zES7Mwb+MG2m6OcEte$FFc3vmK2aW)DR`BL<36L*iPxBW3I*uCUQ;t#UVLtW+8O+7j z&@%HdgK`893sX>Vu~upnLTnW!gf@1F{GyA!MYUq7qe4hp-v$z+!h+Nc3|!j6Zb_3@&NdAl5*lev-RyhO= zT$HvOe*l3t>7A@@U?d^coiE_t2IxmAtd>bvmNszq0OiYVdEIify$)Dtk~_C>PUEC} z;w66D?UnmhxTdiP8S?s#vk&C^woHlLuZ; zIf~1ETKV`e7rk|}a}NXpO&+_~{Nkerz`j9qRmm=3{w%ecI)wRqB%3`v$YQfsxV%V>W|x|!8tDPxr-RgE zUY|LV*7Wzee0-y21-XLjlVq4Zg;A}3A1B=lY7Mi+z*ianqcN@>t+@|OS808Bp8#Ew zcCO?fzUCijvznc!Xrt|M9?2=~mkutpBCY*a!`&P&(3X4o?CD9WQ(fvudd9H;7m%P% zwY0EYue|gWI2_Y={L5Ey`xX7&$g9BR)B0yeq8zvCT_Z{0;1j)XqKwD8n$oKC*;_06 z)Q8;fVzZvy-UB$HFJ-!c3&Z+SZUd0)A;0ER?UqBY^#gO?Q>*6U{pi<9goQ;)X>RqZ zWjZp!i#Sgi`hIqkTJ$j6M27BRLzLE=ipFNG4|lBNlN{EkPAhQZhRu_|7Es1*MNKWh zOu$xr-3g>l*j|2ycjnq->bLkDn{3CE-vYZswn66<5FE9gF}S^7t+Z_MDcJ{jss5(6 ROV7vmWb`KDIX8=n{{x2TD475N diff --git a/src/translations/bitmessage_ja.ts b/src/translations/bitmessage_ja.ts index 65a5c57d..3aa2723e 100644 --- a/src/translations/bitmessage_ja.ts +++ b/src/translations/bitmessage_ja.ts @@ -1841,37 +1841,37 @@ The 'Random Number' option is selected by default but deterministic ad 接続数: - + Since startup: 起動日時: - + Processed 0 person-to-person messages. 0 通の1対1のメッセージを処理しました。 - + Processed 0 public keys. 0 件の公開鍵を処理しました。 - + Processed 0 broadcasts. 0 件の配信を処理しました。 - + Inventory lookups per second: 0 毎秒のインベントリ検索: 0 - + Objects to be synced: 同期する必要のあるオブジェクト: - + Stream # ストリーム # @@ -1881,37 +1881,37 @@ The 'Random Number' option is selected by default but deterministic ad - + Since startup on %1 起動日時 %1 - + Down: %1/s Total: %2 ダウン: %1/秒 合計: %2 - + Up: %1/s Total: %2 アップ: %1/秒 合計: %2 - + Total Connections: %1 接続数: %1 - + Inventory lookups per second: %1 毎秒のインベントリ検索: %1 - + Up: 0 kB/s アップ: 0 kB/秒 - + Down: 0 kB/s ダウン: 0 kB/秒 @@ -1921,42 +1921,47 @@ The 'Random Number' option is selected by default but deterministic ad ネットワークの状態 - + byte(s) バイト - + Object(s) to be synced: %n 同期する必要のあるオブジェクト: %n - + Processed %n person-to-person message(s). %n 通の1対1のメッセージを処理しました。 - + Processed %n broadcast message(s). %n 件の配信を処理しました。 - + Processed %n public key(s). %n 件の公開鍵を処理しました。 - + Peer ピア - + + Rating + 評価 + + + User agent ユーザーエージェント - + TLS TLS From 83e60d231a60195c2d01e0f13665bd00cd751471 Mon Sep 17 00:00:00 2001 From: PyBitmessage Translations Date: Thu, 6 Jul 2017 01:22:28 +0200 Subject: [PATCH 139/407] Auto-updated language pl from transifex --- src/translations/bitmessage_pl.qm | Bin 89830 -> 89888 bytes src/translations/bitmessage_pl.ts | 49 ++++++++++++++++-------------- 2 files changed, 27 insertions(+), 22 deletions(-) diff --git a/src/translations/bitmessage_pl.qm b/src/translations/bitmessage_pl.qm index ad4818c65e659d34fa86c17fb019d922afa09297..ab80193018c5ae2ea21ef7d311873aab5f66d209 100644 GIT binary patch delta 923 zcmW-fZA@Eb6vzLkz4x~F-cm+kl$X%5MOHCk6&Bjb*4Ei|mc_L}!s-;JY`hE-ifm$( zMK;r*OkDywt1ra8pw7tRz-bo7lIr$>2p{SfvY4R|HDRb>j75nrkc_2q!2cR63|M6haCY$;Y@QkQWO~9d@pqiq=x@dkA?Jbk1}*ndI2@`lg}PLh}U zIZaNw#<$5$-*S<`O=;d^aMC{RFhmUD98MXI(Q)<}oixu4#z20=Fl%iDnL~zK(h*?6 zV6?m?gQ|P^ve89-JZH3LoichldqJCvjlM`RXt$AN$#3wj;qQHB;UB0 z&HJ?!jEn%^-;u)U-+==i(t+$#AZ^Gc?~*#Ho>AI$Om5Mxku4V5L811j{q}g5Fr5$P#mx8Rd z|4<3c&;76Dcu?!D{}uGGNgG=H2bh}DM*V+_y{EJ-0B!62iHp4}3K% zUfQJ(%>+S?c3xGT8`kujFE4{0OX{=XBv87dFWFqcZxi}baXZLe!j+m=OCgEo!wRLSh_{i;mqKdSeo*uZUKoo3;R|>vy-uu;qgj1N;9(x12sLq* zy!0O5rXZc>Vkt-;ah>EPKX*z=Y4guKAswV7M=f3&*`jAg9I`blMHy}sH#cz=T zIguOzZrqV;vVQDOnrXb4+QMW1IsY5drtXtV?I#AYJP`cJu|B| zp1v!E`yZ+JfzjPO2MS&{ z27mnrn4C1u#r_sS_i{jY(LS!$z0}67x>xTq?Qe;O6z<_ZJst{}$&1Ut@IABP-x1LI z8|ID!8$kLhvv+@wNc5t4?CWXZ_$G7kT0)q(mX~y|@2WZV`hqZPhdEs{0F>S}=be6F z@q#(OvIA7U$WbGr_u5{m9TJ*8&KO=g%0ot+p5&b2*Du+AJaA3a?RU1DZM25>nkjN} z)(lc7FPUATio81wj|-i6-m)hTw4%!qEO{8vS2!wm?*)a)Q90=Wt*LRWe?-)xNaG<} z#QlWh==2vr<}=5zXG%n?JI-X6L65cbZCgZ5?-IQk>nXF3m)Xgsji(=?v$g*L-g5eE diff --git a/src/translations/bitmessage_pl.ts b/src/translations/bitmessage_pl.ts index 93f72222..02571284 100644 --- a/src/translations/bitmessage_pl.ts +++ b/src/translations/bitmessage_pl.ts @@ -1847,37 +1847,37 @@ Generowanie adresów 'losowych' jest wybrane domyślnie, jednak determ Wszystkich połączeń: - + Since startup: Od startu: - + Processed 0 person-to-person messages. Przetworzono 0 wiadomości zwykłych. - + Processed 0 public keys. Przetworzono 0 kluczy publicznych. - + Processed 0 broadcasts. Przetworzono 0 wiadomości przekazów. - + Inventory lookups per second: 0 Zapytań o elementy na sekundę: 0 - + Objects to be synced: Obiektów do zsynchronizowania: - + Stream # Strumień # @@ -1887,37 +1887,37 @@ Generowanie adresów 'losowych' jest wybrane domyślnie, jednak determ - + Since startup on %1 Od startu programu o %1 - + Down: %1/s Total: %2 Pobieranie: %1/s W całości: %2 - + Up: %1/s Total: %2 Wysyłanie: %1/s W całości: %2 - + Total Connections: %1 Wszystkich połączeń: %1 - + Inventory lookups per second: %1 Zapytań o elementy na sekundę: %1 - + Up: 0 kB/s Wysyłanie: 0 kB/s - + Down: 0 kB/s Pobieranie: 0 kB/s @@ -1927,42 +1927,47 @@ Generowanie adresów 'losowych' jest wybrane domyślnie, jednak determ Stan sieci - + byte(s) bajtbajtówbajtówbajtów - + Object(s) to be synced: %n Jeden obiekt do zsynchronizowaniaObieków do zsynchronizowania: %nObieków do zsynchronizowania: %nObieków do zsynchronizowania: %n - + Processed %n person-to-person message(s). Przetworzono %n wiadomość zwykłą.Przetworzono %n wiadomości zwykłych.Przetworzono %n wiadomości zwykłych.Przetworzono %n wiadomości zwykłych. - + Processed %n broadcast message(s). Przetworzono %n wiadomość przekazów.Przetworzono %n wiadomości przekazów.Przetworzono %n wiadomości przekazów.Przetworzono %n wiadomości przekazów. - + Processed %n public key(s). Przetworzono %n klucz publiczny.Przetworzono %n kluczy publicznych.Przetworzono %n kluczy publicznych.Przetworzono %n kluczy publicznych. - + Peer Użytkownik - + + Rating + Ocena + + + User agent Klient - + TLS TLS From 00a4558971f733f4c1a94d913227aeba229c43a8 Mon Sep 17 00:00:00 2001 From: PyBitmessage Translations Date: Thu, 6 Jul 2017 01:22:19 +0200 Subject: [PATCH 140/407] Auto-updated language eo from transifex --- src/translations/bitmessage_eo.qm | Bin 86609 -> 86675 bytes src/translations/bitmessage_eo.ts | 67 ++++++++++++++++-------------- 2 files changed, 36 insertions(+), 31 deletions(-) diff --git a/src/translations/bitmessage_eo.qm b/src/translations/bitmessage_eo.qm index 073654d4927ecb567df87b758b63d8c5f3be1f5a..2f3af4ec956873f5fea32a91fa79c3f9b079d1c3 100644 GIT binary patch delta 3648 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 delta 3587 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 diff --git a/src/translations/bitmessage_eo.ts b/src/translations/bitmessage_eo.ts index 3127cfe3..7181d678 100644 --- a/src/translations/bitmessage_eo.ts +++ b/src/translations/bitmessage_eo.ts @@ -131,7 +131,7 @@ Bonvolu entajpi deziratan retpoŝtadreson (kune kun @mailchuck.com) sube: @@ -278,7 +278,7 @@ Bonvolu entajpi deziratan retpoŝtadreson (kune kun @mailchuck.com) sube: One of your addresses, %1, is an old version 1 address. Version 1 addresses are no longer supported. May we delete it now? - Iu de viaj adresoj, %1, estas malnova versio 1 adreso. Versioj 1 adresoj ne estas jam subtenataj. Ĉu ni povas forviŝi ĝin? + Iu de viaj adresoj, %1, estas malnova versio 1 adreso. Versioj 1 adresoj ne estas jam subtenataj. Ĉu ni povas forigi ĝin? @@ -977,7 +977,7 @@ Are you sure you want to delete the channel? Fetch Namecoin ID - Venigu Namecoin ID + Venigi Namecoin ID @@ -1122,7 +1122,7 @@ Are you sure you want to delete the channel? Waiting for PoW to finish... %1% - Atendado ĝis laborpruvo finos… %1% + Atendado ĝis laborpruvo finiĝos… %1% @@ -1192,7 +1192,7 @@ Are you sure you want to delete the channel? %1 is already in 'Your Identities'. Not adding it again. - %1 jam estas en 'Viaj Identigoj'. Ĝi ne estos aldonita ree. + %1 jam estas en ‘Viaj Identigoj’. Ĝi ne estos aldonita ree. @@ -1547,8 +1547,8 @@ Eble vi devas ĝisdatigi Bitmesaĝon. Here you may generate as many addresses as you like. Indeed, creating and abandoning addresses is encouraged. You may generate addresses by using either random numbers or by using a passphrase. If you use a passphrase, the address is called a "deterministic" address. The 'Random Number' option is selected by default but deterministic addresses have several pros and cons: - Tie ĉi vi povas generi tiom da adresoj, kiom vi volas. Ververe kreado kaj forlasado de adresoj estas konsilinda. Vi povas krei adresojn uzante hazardajn nombrojn aŭ pasfrazon. Se vi uzos pasfrazon, la adreso estas nomita kiel 'antaŭkalkulebla' (determinisma) adreso. -La 'hazardnombra' adreso estas antaŭagordita, sed antaŭkalkuleblaj adresoj havas kelkajn bonaĵojn kaj malbonaĵojn: + Tie ĉi vi povas generi tiom da adresoj, kiom vi volas. Ververe kreado kaj forlasado de adresoj estas konsilinda. Vi povas krei adresojn uzante hazardajn nombrojn aŭ pasfrazon. Se vi uzos pasfrazon, la adreso estas nomita kiel ‘antaŭkalkulebla’ (determinisma) adreso. +La ‘hazardnombra’ adreso estas antaŭagordita, sed antaŭkalkuleblaj adresoj havas kelkajn bonaĵojn kaj malbonaĵojn: @@ -1840,37 +1840,37 @@ La 'hazardnombra' adreso estas antaŭagordita, sed antaŭkalkuleblaj a Ĉiuj konektoj: - + Since startup: Ekde starto: - + Processed 0 person-to-person messages. Pritraktis 0 inter-personajn mesaĝojn. - + Processed 0 public keys. Pritraktis 0 publikajn ŝlosilojn. - + Processed 0 broadcasts. Pritraktis 0 elsendojn. - + Inventory lookups per second: 0 Petoj pri inventaro en sekundo: 0 - + Objects to be synced: Samtempigotaj eroj: - + Stream # Fluo # @@ -1880,37 +1880,37 @@ La 'hazardnombra' adreso estas antaŭagordita, sed antaŭkalkuleblaj a - + Since startup on %1 Ekde lanĉo de la programo je %1 - + Down: %1/s Total: %2 Elŝuto: %1/s Sume: %2 - + Up: %1/s Total: %2 Alŝuto: %1/s Sume: %2 - + Total Connections: %1 Ĉiuj konektoj: %1 - + Inventory lookups per second: %1 Petoj pri inventaro en sekundo: %1 - + Up: 0 kB/s Alŝuto: 0 kB/s - + Down: 0 kB/s Elŝuto: 0 kB/s @@ -1920,42 +1920,47 @@ La 'hazardnombra' adreso estas antaŭagordita, sed antaŭkalkuleblaj a Reta stato - + byte(s) bitokobitokoj - + Object(s) to be synced: %n Objekto por samtempigi: %nObjektoj por samtempigi: %n - + Processed %n person-to-person message(s). Pritraktis %n inter-personan mesaĝon.Pritraktis %n inter-personajn mesaĝojn. - + Processed %n broadcast message(s). Pritraktis %n elsendon.Pritraktis %n elsendojn. - + Processed %n public key(s). Pritraktis %n publikan ŝlosilon.Pritraktis %n publikajn ŝlosilojn. - + Peer Samtavolano - + + Rating + Takso + + + User agent Klienta aplikaĵo - + TLS TLS From 4536e44b8c3004710a7f3968b8cbc462df70f4d2 Mon Sep 17 00:00:00 2001 From: Peter Surda Date: Thu, 6 Jul 2017 19:35:40 +0200 Subject: [PATCH 141/407] Thread names propagate to system - the thread names should now show up in the monitoring tools of operating systems (tested on linux) --- src/bitmessagemain.py | 6 ++++-- src/helper_threading.py | 15 ++++++++++++++- 2 files changed, 18 insertions(+), 3 deletions(-) diff --git a/src/bitmessagemain.py b/src/bitmessagemain.py index 88fa3c1d..b142676b 100755 --- a/src/bitmessagemain.py +++ b/src/bitmessagemain.py @@ -63,7 +63,7 @@ from network.downloadthread import DownloadThread # Helper Functions import helper_bootstrap import helper_generic -from helper_threading import * +import helper_threading def connectToStream(streamNumber): @@ -153,7 +153,7 @@ def _fixSocket(): socket.IPV6_V6ONLY = 27 # This thread, of which there is only one, runs the API. -class singleAPI(threading.Thread, StoppableThread): +class singleAPI(threading.Thread, helper_threading.StoppableThread): def __init__(self): threading.Thread.__init__(self, name="singleAPI") self.initStop() @@ -204,6 +204,8 @@ class Main: self.setSignalHandler() + helper_threading.set_thread_name("MainThread") + helper_bootstrap.knownNodes() # Start the address generation thread addressGeneratorThread = addressGenerator() diff --git a/src/helper_threading.py b/src/helper_threading.py index 599d297d..cd51557f 100644 --- a/src/helper_threading.py +++ b/src/helper_threading.py @@ -1,4 +1,17 @@ import threading +try: + import prctl + def set_thread_name(name): prctl.set_name(name) + + def _thread_name_hack(self): + set_thread_name(self.name) + threading.Thread.__bootstrap_original__(self) + + threading.Thread.__bootstrap_original__ = threading.Thread._Thread__bootstrap + threading.Thread._Thread__bootstrap = _thread_name_hack +except ImportError: + log('WARN: prctl module is not installed. You will not be able to see thread names') + def set_thread_name(name): pass class StoppableThread(object): def initStop(self): @@ -7,4 +20,4 @@ class StoppableThread(object): def stopThread(self): self._stopped = True - self.stop.set() \ No newline at end of file + self.stop.set() From a2b8867c1aa57ef5b68b0ad3d86937a4eca70ea8 Mon Sep 17 00:00:00 2001 From: Peter Surda Date: Thu, 6 Jul 2017 19:36:04 +0200 Subject: [PATCH 142/407] Tooltips for network status columns --- src/bitmessageqt/networkstatus.ui | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/src/bitmessageqt/networkstatus.ui b/src/bitmessageqt/networkstatus.ui index f9cc57c4..f05e5f62 100644 --- a/src/bitmessageqt/networkstatus.ui +++ b/src/bitmessageqt/networkstatus.ui @@ -116,26 +116,41 @@ Peer + + IP address or hostname + Rating + + PyBitmessage tracks the success rate of connection attempts to individual nodes. The rating ranges from -1 to 1 and affects the likelihood of selecting the node in the future + User agent + + Peer's self-reported software + TLS + + Connection encryption + Stream # + + List of streams negotiated between you and the peer + From ba4162d7fe1a81952ce105431a63d9ede84021ba Mon Sep 17 00:00:00 2001 From: Peter Surda Date: Thu, 6 Jul 2017 19:45:36 +0200 Subject: [PATCH 143/407] Asyncore update - get rid of per-connection writeQueue/receiveQueue, and instead use strings and locking - minor code cleanup - all state handlers now should set expectBytes - almost all data processing happens in ReceiveDataThread, and AsyncoreThread is almost only I/O (plus TLS). AsyncoreThread simply puts the connection object into the queue when it has some data for processing - allow poll, epoll and kqueue handlers. kqueue is untested and unoptimised, poll and epoll seem to work ok (linux) - stack depth threshold handler in decode_payload_content, this is recursive and I think was causing occasional RuntimeErrors. Fixes #964 - longer asyncore loops, as now data is handled in ReceiveDataThread - randomise node order when deciding what to download. Should prevent retries being stuck to the same node - socks cleanup (socks5 works ok, socks4a untested but should work too) --- src/network/advanceddispatcher.py | 66 +++++++++--------------- src/network/announcethread.py | 2 +- src/network/asyncore_pollchoose.py | 74 +++++++++++++++------------ src/network/bmproto.py | 63 ++++++++++++----------- src/network/connectionpool.py | 11 ++-- src/network/downloadthread.py | 8 ++- src/network/invthread.py | 2 +- src/network/receivequeuethread.py | 81 ++++++------------------------ src/network/socks4a.py | 41 ++++++++------- src/network/socks5.py | 77 +++++++++++++--------------- src/network/tcp.py | 51 ++++++++++++++++--- src/network/tls.py | 36 ++++++++----- src/network/udp.py | 18 +++---- src/queues.py | 1 + 14 files changed, 257 insertions(+), 274 deletions(-) diff --git a/src/network/advanceddispatcher.py b/src/network/advanceddispatcher.py index 0945d764..57bd4f41 100644 --- a/src/network/advanceddispatcher.py +++ b/src/network/advanceddispatcher.py @@ -1,6 +1,7 @@ import Queue import socket import sys +import threading import time import asyncore_pollchoose as asyncore @@ -14,41 +15,43 @@ class AdvancedDispatcher(asyncore.dispatcher): asyncore.dispatcher.__init__(self, sock) self.read_buf = b"" self.write_buf = b"" - self.writeQueue = Queue.Queue() - self.receiveQueue = Queue.Queue() self.state = "init" self.lastTx = time.time() self.sentBytes = 0 self.receivedBytes = 0 self.expectBytes = 0 + self.readLock = threading.RLock() + self.writeLock = threading.RLock() + + def append_write_buf(self, data): + if data: + with self.writeLock: + self.write_buf += data def slice_write_buf(self, length=0): if length > 0: - self.write_buf = self.write_buf[length:] + with self.writeLock: + self.write_buf = self.write_buf[length:] def slice_read_buf(self, length=0): if length > 0: - self.read_buf = self.read_buf[length:] - - def read_buf_sufficient(self, length=0): - if len(self.read_buf) < length: - return False - return True + with self.readLock: + self.read_buf = self.read_buf[length:] def process(self): - if self.state != "tls_handshake" and not self.read_buf: - return if not self.connected: return - maxLoop = 20 - while maxLoop > 0: + loop = 0 + while len(self.read_buf) >= self.expectBytes: + loop += 1 + if loop > 1000: + logger.error("Stuck at state %s, report this bug please", self.state) + break try: if getattr(self, "state_" + str(self.state))() is False: break except AttributeError: - # missing state raise - maxLoop -= 1 def set_state(self, state, length=0, expectBytes=0): self.expectBytes = expectBytes @@ -57,7 +60,7 @@ class AdvancedDispatcher(asyncore.dispatcher): def writable(self): return asyncore.dispatcher.writable(self) and \ - (self.connecting or len(self.write_buf) > 0 or not self.writeQueue.empty()) + (self.connecting or self.write_buf) def readable(self): return asyncore.dispatcher.readable(self) and \ @@ -68,28 +71,20 @@ class AdvancedDispatcher(asyncore.dispatcher): downloadBytes = AdvancedDispatcher._buf_len if asyncore.maxDownloadRate > 0: downloadBytes = asyncore.downloadBucket - if self.expectBytes > 0 and downloadBytes > self.expectBytes: - downloadBytes = self.expectBytes + if self.expectBytes > 0 and downloadBytes > self.expectBytes - len(self.read_buf): + downloadBytes = self.expectBytes - len(self.read_buf) if downloadBytes > 0: newData = self.recv(downloadBytes) self.receivedBytes += len(newData) - if self.expectBytes > 0: - self.expectBytes -= len(newData) asyncore.update_received(len(newData)) - self.read_buf += newData - self.process() + with self.readLock: + self.read_buf += newData def handle_write(self): self.lastTx = time.time() bufSize = AdvancedDispatcher._buf_len if asyncore.maxUploadRate > 0: bufSize = asyncore.uploadBucket - while len(self.write_buf) < bufSize: - try: - self.write_buf += self.writeQueue.get(False) - self.writeQueue.task_done() - except Queue.Empty: - break if bufSize <= 0: return if self.write_buf: @@ -107,25 +102,12 @@ class AdvancedDispatcher(asyncore.dispatcher): def handle_connect(self): self.lastTx = time.time() - self.process() def state_close(self): - pass + return False def handle_close(self): self.read_buf = b"" self.write_buf = b"" self.state = "close" - while True: - try: - self.writeQueue.get(False) - self.writeQueue.task_done() - except Queue.Empty: - break - while True: - try: - self.receiveQueue.get(False) - self.receiveQueue.task_done() - except Queue.Empty: - break asyncore.dispatcher.close(self) diff --git a/src/network/announcethread.py b/src/network/announcethread.py index 3adcae48..354cfaa8 100644 --- a/src/network/announcethread.py +++ b/src/network/announcethread.py @@ -30,4 +30,4 @@ class AnnounceThread(threading.Thread, StoppableThread): for connection in BMConnectionPool().udpSockets.values(): for stream in state.streamsInWhichIAmParticipating: addr = (stream, state.Peer('127.0.0.1', BMConfigParser().safeGetInt("bitmessagesettings", "port")), time.time()) - connection.writeQueue.put(BMProto.assembleAddr([addr])) + connection.append_write_buf(BMProto.assembleAddr([addr])) diff --git a/src/network/asyncore_pollchoose.py b/src/network/asyncore_pollchoose.py index 3f188812..07b2c120 100644 --- a/src/network/asyncore_pollchoose.py +++ b/src/network/asyncore_pollchoose.py @@ -249,13 +249,16 @@ def poll_poller(timeout=0.0, map=None): newflags |= OP_WRITE else: newflags &= ~ OP_WRITE - if newflags != obj.flags: - obj.flags = newflags - if obj.poller_registered: - poll_poller.pollster.modify(fd, flags) - else: - poll_poller.pollster.register(fd, flags) - obj.poller_registered = True + if newflags != obj.poller_flags: + obj.poller_flags = newflags + try: + if obj.poller_registered: + poll_poller.pollster.modify(fd, flags) + else: + poll_poller.pollster.register(fd, flags) + obj.poller_registered = True + except IOError: + pass try: r = poll_poller.pollster.poll(timeout) except KeyboardInterrupt: @@ -292,16 +295,19 @@ def epoll_poller(timeout=0.0, map=None): newflags |= OP_WRITE else: newflags &= ~ OP_WRITE - if newflags != obj.flags: - obj.flags = newflags + if newflags != obj.poller_flags: + obj.poller_flags = newflags # Only check for exceptions if object was either readable # or writable. flags |= select.POLLERR | select.POLLHUP | select.POLLNVAL - if obj.poller_registered: - epoll_poller.pollster.modify(fd, flags) - else: - epoll_poller.pollster.register(fd, flags) - obj.poller_registered = True + try: + if obj.poller_registered: + epoll_poller.pollster.modify(fd, flags) + else: + epoll_poller.pollster.register(fd, flags) + obj.poller_registered = True + except IOError: + pass try: r = epoll_poller.pollster.poll(timeout) except select.error, err: @@ -329,9 +335,12 @@ def kqueue_poller(timeout=0.0, map=None): if obj.writable(): filter |= select.KQ_FILTER_WRITE if filter: - ev = select.kevent(fd, filter=filter, flags=flags) - kqueue.control([ev], 0) - selectables += 1 + try: + ev = select.kevent(fd, filter=filter, flags=flags) + kqueue.control([ev], 0) + selectables += 1 + except IOError: + pass events = kqueue.control(None, selectables, timeout) for event in random.sample(events, len(events)): @@ -347,25 +356,23 @@ def kqueue_poller(timeout=0.0, map=None): def loop(timeout=30.0, use_poll=False, map=None, count=None, - poller=select_poller): + poller=None): if map is None: map = socket_map # code which grants backward compatibility with "use_poll" # argument which should no longer be used in favor of # "poller" - if hasattr(select, 'epoll'): - poller = epoll_poller - elif hasattr(select, 'kqueue'): - poller = kqueue_poller - elif hasattr(select, 'poll'): - poller = poll_poller - elif hasattr(select, 'select'): - poller = select_poller - poller = select_poller - -# print "Poll loop using %s" % (poller.__name__) + if poller is None: + if hasattr(select, 'epoll'): + poller = epoll_poller + elif hasattr(select, 'kqueue'): + poller = kqueue_poller + elif hasattr(select, 'poll'): + poller = poll_poller + elif hasattr(select, 'select'): + poller = select_poller if count is None: while map: @@ -400,7 +407,7 @@ class dispatcher: addr = None ignore_log_types = frozenset(['warning']) poller_registered = False - flags = 0 + poller_flags = 0 # don't do network IO with a smaller bucket than this minTx = 1500 @@ -456,23 +463,26 @@ class dispatcher: if map is None: map = self._map map[self._fileno] = self + self.poller_flags = 0 def del_channel(self, map=None): fd = self._fileno if map is None: map = self._map + self.poller_flags = 0 + self.poller_registered = False if fd in map: #self.log_info('closing channel %d:%s' % (fd, self)) del map[fd] self._fileno = None try: epoll_poller.pollster.unregister(fd) - except (AttributeError, KeyError, TypeError): + except (AttributeError, KeyError, TypeError, IOError): # no epoll used, or not registered pass try: poll_poller.pollster.unregister(fd) - except (AttributeError, KeyError, TypeError): + except (AttributeError, KeyError, TypeError, IOError): # no poll used, or not registered pass diff --git a/src/network/bmproto.py b/src/network/bmproto.py index abf2f0ec..e00d6d0a 100644 --- a/src/network/bmproto.py +++ b/src/network/bmproto.py @@ -5,6 +5,7 @@ import math import time import socket import struct +import sys from addresses import calculateInventoryHash from bmconfigparser import BMConfigParser @@ -67,15 +68,12 @@ class BMProto(AdvancedDispatcher, ObjectTracker): self.object = None def state_bm_header(self): - if len(self.read_buf) < protocol.Header.size: - #print "Length below header size" - return False self.magic, self.command, self.payloadLength, self.checksum = protocol.Header.unpack(self.read_buf[:protocol.Header.size]) self.command = self.command.rstrip('\x00') if self.magic != 0xE9BEB4D9: # skip 1 byte in order to sync + self.set_state("bm_header", length=1) self.bm_proto_reset() - self.set_state("bm_header", length=1, expectBytes=protocol.Header.size) logger.debug("Bad magic") self.handle_close("Bad magic") return False @@ -85,10 +83,6 @@ class BMProto(AdvancedDispatcher, ObjectTracker): return True def state_bm_command(self): - if len(self.read_buf) < self.payloadLength: - #print "Length below announced object length" - return False - #logger.debug("%s:%i: command %s (%ib)", self.destination.host, self.destination.port, self.command, self.payloadLength) self.payload = self.read_buf[:self.payloadLength] if self.checksum != hashlib.sha512(self.payload).digest()[0:4]: logger.debug("Bad checksum, ignoring") @@ -127,7 +121,7 @@ class BMProto(AdvancedDispatcher, ObjectTracker): self.handle_close("Invalid command %s" % (self.command)) return False if retval: - self.set_state("bm_header", length=self.payloadLength, expectBytes=protocol.Header.size) + self.set_state("bm_header", length=self.payloadLength) self.bm_proto_reset() # else assume the command requires a different state to follow return True @@ -173,6 +167,12 @@ class BMProto(AdvancedDispatcher, ObjectTracker): retval = [] size = None i = 0 + try: + sys._getframe(200) + logger.error("Stack depth warning, pattern: %s", pattern) + return + except ValueError: + pass while i < len(pattern): if pattern[i] in "0123456789" and (i == 0 or pattern[i-1] not in "lL"): @@ -237,8 +237,13 @@ class BMProto(AdvancedDispatcher, ObjectTracker): # skip? if time.time() < self.skipUntil: return True + #TODO make this more asynchronous and allow reordering for i in items: - self.receiveQueue.put(("object", i)) + try: + self.append_write_buf(protocol.CreatePacket('object', Inventory()[i].payload)) + except KeyError: + self.antiIntersectionDelay() + logger.info('%s asked for an object we don\'t have.', self.destination) return True def bm_command_inv(self): @@ -251,7 +256,7 @@ class BMProto(AdvancedDispatcher, ObjectTracker): pass for i in items: - self.receiveQueue.put(("inv", i)) + self.handleReceivedInventory(i) return True @@ -321,7 +326,7 @@ class BMProto(AdvancedDispatcher, ObjectTracker): return True def bm_command_ping(self): - self.writeQueue.put(protocol.CreatePacket('pong')) + self.append_write_buf(protocol.CreatePacket('pong')) return True def bm_command_pong(self): @@ -332,11 +337,10 @@ class BMProto(AdvancedDispatcher, ObjectTracker): self.verackReceived = True if self.verackSent: if self.isSSL: - self.set_state("tls_init", self.payloadLength) - self.bm_proto_reset() + self.set_state("tls_init", length=self.payloadLength, expectBytes=0) return False - self.set_connection_fully_established() - return True + self.set_state("connection_fully_established", length=self.payloadLength, expectBytes=0) + return False return True def bm_command_version(self): @@ -345,20 +349,20 @@ class BMProto(AdvancedDispatcher, ObjectTracker): self.nonce = struct.pack('>Q', self.nonce) self.timeOffset = self.timestamp - int(time.time()) logger.debug("remoteProtocolVersion: %i", self.remoteProtocolVersion) - logger.debug("services: %08X", self.services) + logger.debug("services: 0x%08X", self.services) logger.debug("time offset: %i", self.timestamp - int(time.time())) logger.debug("my external IP: %s", self.sockNode.host) - logger.debug("remote node incoming port: %i", self.peerNode.port) + logger.debug("remote node incoming address: %s:%i", self.destination.host, self.peerNode.port) logger.debug("user agent: %s", self.userAgent) logger.debug("streams: [%s]", ",".join(map(str,self.streams))) if not self.peerValidityChecks(): # TODO ABORT return True #shared.connectedHostsList[self.destination] = self.streams[0] - self.writeQueue.put(protocol.CreatePacket('verack')) + self.append_write_buf(protocol.CreatePacket('verack')) self.verackSent = True if not self.isOutbound: - self.writeQueue.put(protocol.assembleVersionMessage(self.destination.host, self.destination.port, \ + self.append_write_buf(protocol.assembleVersionMessage(self.destination.host, self.destination.port, \ network.connectionpool.BMConnectionPool().streams, True)) #print "%s:%i: Sending version" % (self.destination.host, self.destination.port) if ((self.services & protocol.NODE_SSL == protocol.NODE_SSL) and @@ -366,29 +370,28 @@ class BMProto(AdvancedDispatcher, ObjectTracker): self.isSSL = True if self.verackReceived: if self.isSSL: - self.set_state("tls_init", self.payloadLength) - self.bm_proto_reset() + self.set_state("tls_init", length=self.payloadLength, expectBytes=0) return False - self.set_connection_fully_established() - return True + self.set_state("connection_fully_established", length=self.payloadLength, expectBytes=0) + return False return True def peerValidityChecks(self): if self.remoteProtocolVersion < 3: - self.writeQueue.put(protocol.assembleErrorMessage(fatal=2, + self.append_write_buf(protocol.assembleErrorMessage(fatal=2, errorText="Your is using an old protocol. Closing connection.")) logger.debug ('Closing connection to old protocol version %s, node: %s', str(self.remoteProtocolVersion), str(self.destination)) return False if self.timeOffset > BMProto.maxTimeOffset: - self.writeQueue.put(protocol.assembleErrorMessage(fatal=2, + self.append_write_buf(protocol.assembleErrorMessage(fatal=2, errorText="Your time is too far in the future compared to mine. Closing connection.")) logger.info("%s's time is too far in the future (%s seconds). Closing connection to it.", self.destination, self.timeOffset) shared.timeOffsetWrongCount += 1 return False elif self.timeOffset < -BMProto.maxTimeOffset: - self.writeQueue.put(protocol.assembleErrorMessage(fatal=2, + self.append_write_buf(protocol.assembleErrorMessage(fatal=2, errorText="Your time is too far in the past compared to mine. Closing connection.")) logger.info("%s's time is too far in the past (timeOffset %s seconds). Closing connection to it.", self.destination, self.timeOffset) @@ -397,7 +400,7 @@ class BMProto(AdvancedDispatcher, ObjectTracker): else: shared.timeOffsetWrongCount = 0 if not self.streams: - self.writeQueue.put(protocol.assembleErrorMessage(fatal=2, + self.append_write_buf(protocol.assembleErrorMessage(fatal=2, errorText="We don't have shared stream interests. Closing connection.")) logger.debug ('Closed connection to %s because there is no overlapping interest in streams.', str(self.destination)) @@ -405,7 +408,7 @@ class BMProto(AdvancedDispatcher, ObjectTracker): if self.destination in network.connectionpool.BMConnectionPool().inboundConnections: try: if not protocol.checkSocksIP(self.destination.host): - self.writeQueue.put(protocol.assembleErrorMessage(fatal=2, + self.append_write_buf(protocol.assembleErrorMessage(fatal=2, errorText="Too many connections from your IP. Closing connection.")) logger.debug ('Closed connection to %s because we are already connected to that IP.', str(self.destination)) @@ -413,7 +416,7 @@ class BMProto(AdvancedDispatcher, ObjectTracker): except: pass if self.nonce == protocol.eightBytesOfRandomDataUsedToDetectConnectionsToSelf: - self.writeQueue.put(protocol.assembleErrorMessage(fatal=2, + self.append_write_buf(protocol.assembleErrorMessage(fatal=2, errorText="I'm connected to myself. Closing connection.")) logger.debug ("Closed connection to %s because I'm connected to myself.", str(self.destination)) diff --git a/src/network/connectionpool.py b/src/network/connectionpool.py index c4aa53d8..4c2b4c6c 100644 --- a/src/network/connectionpool.py +++ b/src/network/connectionpool.py @@ -19,7 +19,6 @@ import state @Singleton class BMConnectionPool(object): - def __init__(self): asyncore.set_rates( BMConfigParser().safeGetInt("bitmessagesettings", "maxdownloadrate") * 1024, @@ -30,7 +29,7 @@ class BMConnectionPool(object): self.udpSockets = {} self.streams = [] self.lastSpawned = 0 - self.spawnWait = 0.3 + self.spawnWait = 2 self.bootstrapped = False def handleReceivedObject(self, streamNumber, hashid, connection = None): @@ -187,12 +186,10 @@ class BMConnectionPool(object): i.handle_close() logger.info('Stopped udp sockets.') -# while len(asyncore.socket_map) > 0 and state.shutdown == 0: -# print "loop, state = %s" % (proxy.state) loopTime = float(self.spawnWait) if self.lastSpawned < time.time() - self.spawnWait: - loopTime = 1.0 - asyncore.loop(timeout=loopTime, count=10) + loopTime = 2.0 + asyncore.loop(timeout=loopTime, count=1000) reaper = [] for i in self.inboundConnections.values() + self.outboundConnections.values(): @@ -201,7 +198,7 @@ class BMConnectionPool(object): minTx -= 300 - 20 if i.lastTx < minTx: if i.fullyEstablished: - i.writeQueue.put(protocol.CreatePacket('ping')) + i.append_write_buf(protocol.CreatePacket('ping')) else: i.handle_close("Timeout (%is)" % (time.time() - i.lastTx)) for i in self.inboundConnections.values() + self.outboundConnections.values() + self.listeningSockets.values() + self.udpSockets.values(): diff --git a/src/network/downloadthread.py b/src/network/downloadthread.py index 9c7e92da..c42d7e1c 100644 --- a/src/network/downloadthread.py +++ b/src/network/downloadthread.py @@ -1,3 +1,4 @@ +import random import threading import time @@ -32,7 +33,10 @@ class DownloadThread(threading.Thread, StoppableThread): def run(self): while not self._stopped: requested = 0 - for i in BMConnectionPool().inboundConnections.values() + BMConnectionPool().outboundConnections.values(): + # Choose downloading peers randomly + connections = BMConnectionPool().inboundConnections.values() + BMConnectionPool().outboundConnections.values() + random.shuffle(connections) + for i in connections: now = time.time() timedOut = now - DownloadThread.requestTimeout # this may take a while, but it needs a consistency so I think it's better to lock a bigger chunk @@ -52,7 +56,7 @@ class DownloadThread(threading.Thread, StoppableThread): self.pending[k] = now payload = addresses.encodeVarint(len(request)) + ''.join(request) - i.writeQueue.put(protocol.CreatePacket('getdata', payload)) + i.append_write_buf(protocol.CreatePacket('getdata', payload)) logger.debug("%s:%i Requesting %i objects", i.destination.host, i.destination.port, len(request)) requested += len(request) if time.time() >= self.lastCleaned + DownloadThread.cleanInterval: diff --git a/src/network/invthread.py b/src/network/invthread.py index 398fecf0..e5ab890a 100644 --- a/src/network/invthread.py +++ b/src/network/invthread.py @@ -42,7 +42,7 @@ class InvThread(threading.Thread, StoppableThread): except KeyError: continue if hashes: - connection.writeQueue.put(protocol.CreatePacket('inv', \ + connection.append_write_buf(protocol.CreatePacket('inv', \ addresses.encodeVarint(len(hashes)) + "".join(hashes))) invQueue.iterate() self.stop.wait(1) diff --git a/src/network/receivequeuethread.py b/src/network/receivequeuethread.py index 442c755a..120d15e2 100644 --- a/src/network/receivequeuethread.py +++ b/src/network/receivequeuethread.py @@ -10,6 +10,7 @@ from helper_threading import StoppableThread from inventory import Inventory from network.connectionpool import BMConnectionPool from network.bmproto import BMProto +from queues import receiveDataQueue import protocol import state @@ -21,73 +22,23 @@ class ReceiveQueueThread(threading.Thread, StoppableThread): logger.info("init receive queue thread") def run(self): - lastprinted = int(time.time()) while not self._stopped and state.shutdown == 0: - if lastprinted < int(time.time()): - lastprinted = int(time.time()) -# try: -# sys._getframe(200) -# logger.error("Stack depth warning") -# except ValueError: -# pass - processed = 0 - for i in BMConnectionPool().inboundConnections.values() + BMConnectionPool().outboundConnections.values(): - if self._stopped: - break - try: - command, args = i.receiveQueue.get(False) - except Queue.Empty: - continue - processed += 1 - try: - getattr(self, "command_" + str(command))(i, args) - i.receiveQueue.task_done() - except AttributeError: - i.receiveQueue.task_done() - # missing command - raise - if processed == 0: - self.stop.wait(2) + try: + connection = receiveDataQueue.get(block=True, timeout=1) + receiveDataQueue.task_done() + except Queue.Empty: + continue - def command_object(self, connection, objHash): - try: - connection.writeQueue.put(protocol.CreatePacket('object', Inventory()[objHash].payload)) - except KeyError: - connection.antiIntersectionDelay() - logger.info('%s asked for an object we don\'t have.', connection.destination) - - def command_biginv(self, connection, dummy): - def sendChunk(): - if objectCount == 0: - return - logger.debug('Sending huge inv message with %i objects to just this one peer', objectCount) - connection.writeQueue.put(protocol.CreatePacket('inv', addresses.encodeVarint(objectCount) + payload)) - - # Select all hashes for objects in this stream. - bigInvList = {} - for stream in connection.streams: - # may lock for a long time, but I think it's better than thousands of small locks - with connection.objectsNewToThemLock: - for objHash in Inventory().unexpired_hashes_by_stream(stream): - bigInvList[objHash] = 0 - connection.objectsNewToThem[objHash] = time.time() - objectCount = 0 - payload = b'' - # Now let us start appending all of these hashes together. They will be - # sent out in a big inv message to our new peer. - for hash, storedValue in bigInvList.items(): - payload += hash - objectCount += 1 - if objectCount >= BMProto.maxObjectCount: - self.sendChunk() - payload = b'' - objectCount = 0 - - # flush - sendChunk() - - def command_inv(self, connection, hashId): - connection.handleReceivedInventory(hashId) + if self._stopped: + break + # cycle as long as there is data + # methods should return False if there isn't enough data, or the connection is to be aborted + try: + while connection.process(): + pass + except AttributeError: + # missing command + logger.error("Unknown state %s, ignoring", connection.state) def stopThread(self): super(ReceiveQueueThread, self).stopThread() diff --git a/src/network/socks4a.py b/src/network/socks4a.py index 350e163e..d6cf2ad8 100644 --- a/src/network/socks4a.py +++ b/src/network/socks4a.py @@ -19,10 +19,9 @@ class Socks4a(Proxy): def state_init(self): self.set_state("auth_done", 0) + return True def state_pre_connect(self): - if not self.read_buf_sufficient(8): - return False # Get the response if self.read_buf[0:1] != chr(0x00).encode(): # bad data @@ -44,14 +43,12 @@ class Socks4a(Proxy): self.__proxypeername = (socket.inet_ntoa(self.ipaddr), self.destination[1]) else: self.__proxypeername = (self.destination[0], self.destport) - self.set_state("proxy_handshake_done", 8) + self.set_state("proxy_handshake_done", length=8) + return True def proxy_sock_name(self): return socket.inet_ntoa(self.__proxysockname[0]) - def state_socks_handshake_done(self): - return False - class Socks4aConnection(Socks4a): def __init__(self, address): @@ -60,33 +57,34 @@ class Socks4aConnection(Socks4a): def state_auth_done(self): # Now we can request the actual connection rmtrslv = False - self.writeQueue.put(struct.pack('>BBH', 0x04, 0x01, self.destination[1])) + self.append_write_buf(struct.pack('>BBH', 0x04, 0x01, self.destination[1])) # If the given destination address is an IP address, we'll # use the IPv4 address request even if remote resolving was specified. try: self.ipaddr = socket.inet_aton(self.destination[0]) - self.writeQueue.put(self.ipaddr) + self.append_write_buf(self.ipaddr) except socket.error: # Well it's not an IP number, so it's probably a DNS name. if Proxy._remote_dns: # Resolve remotely rmtrslv = True self.ipaddr = None - self.writeQueue.put(struct.pack("BBBB", 0x00, 0x00, 0x00, 0x01)) + self.append_write_buf(struct.pack("BBBB", 0x00, 0x00, 0x00, 0x01)) else: # Resolve locally self.ipaddr = socket.inet_aton(socket.gethostbyname(self.destination[0])) - self.writeQueue.put(self.ipaddr) + self.append_write_buf(self.ipaddr) if self._auth: - self.writeQueue.put(self._auth[0]) - self.writeQueue.put(chr(0x00).encode()) + self.append_write_buf(self._auth[0]) + self.append_write_buf(chr(0x00).encode()) if rmtrslv: - self.writeQueue.put(self.destination[0] + chr(0x00).encode()) - self.set_state("pre_connect", 0) + self.append_write_buf(self.destination[0] + chr(0x00).encode()) + self.set_state("pre_connect", length=0, expectBytes=8) + return True def state_pre_connect(self): try: - Socks4a.state_pre_connect(self) + return Socks4a.state_pre_connect(self) except Socks4aError as e: self.handle_close(e.message) @@ -99,13 +97,14 @@ class Socks4aResolver(Socks4a): def state_auth_done(self): # Now we can request the actual connection - self.writeQueue.put(struct.pack('>BBH', 0x04, 0xF0, self.destination[1])) - self.writeQueue.put(struct.pack("BBBB", 0x00, 0x00, 0x00, 0x01)) + self.append_write_buf(struct.pack('>BBH', 0x04, 0xF0, self.destination[1])) + self.append_write_buf(struct.pack("BBBB", 0x00, 0x00, 0x00, 0x01)) if self._auth: - self.writeQueue.put(self._auth[0]) - self.writeQueue.put(chr(0x00).encode()) - self.writeQueue.put(self.host + chr(0x00).encode()) - self.set_state("pre_connect", 0) + self.append_write_buf(self._auth[0]) + self.append_write_buf(chr(0x00).encode()) + self.append_write_buf(self.host + chr(0x00).encode()) + self.set_state("pre_connect", length=0, expectBytes=8) + return True def resolved(self): print "Resolved %s as %s" % (self.host, self.proxy_sock_name()) diff --git a/src/network/socks5.py b/src/network/socks5.py index f2bc83e4..e57e7c6a 100644 --- a/src/network/socks5.py +++ b/src/network/socks5.py @@ -32,28 +32,26 @@ class Socks5(Proxy): def state_init(self): if self._auth: - self.writeQueue.put(struct.pack('BBBB', 0x05, 0x02, 0x00, 0x02)) + self.append_write_buf(struct.pack('BBBB', 0x05, 0x02, 0x00, 0x02)) else: - self.writeQueue.put(struct.pack('BBB', 0x05, 0x01, 0x00)) - self.set_state("auth_1", 0) + self.append_write_buf(struct.pack('BBB', 0x05, 0x01, 0x00)) + self.set_state("auth_1", length=0, expectBytes=2) + return True def state_auth_1(self): - if not self.read_buf_sufficient(2): - return False ret = struct.unpack('BB', self.read_buf) - self.read_buf = self.read_buf[2:] if ret[0] != 5: # general error raise GeneralProxyError(1) elif ret[1] == 0: # no auth required - self.set_state("auth_done", 2) + self.set_state("auth_done", length=2) elif ret[1] == 2: # username/password - self.writeQueue.put(struct.pack('BB', 1, len(self._auth[0])) + \ + self.append_write_buf(struct.pack('BB', 1, len(self._auth[0])) + \ self._auth[0] + struct.pack('B', len(self._auth[1])) + \ self._auth[1]) - self.set_state("auth_needed", 2) + self.set_state("auth_needed", length=2, expectBytes=2) else: if ret[1] == 0xff: # auth error @@ -61,11 +59,10 @@ class Socks5(Proxy): else: # other error raise GeneralProxyError(1) + return True def state_auth_needed(self): - if not self.read_buf_sufficient(2): - return False - ret = struct.unpack('BB', self.read_buf) + ret = struct.unpack('BB', self.read_buf[0:2]) if ret[0] != 1: # general error raise GeneralProxyError(1) @@ -73,11 +70,10 @@ class Socks5(Proxy): # auth error raise Socks5AuthError(3) # all ok - self.set_state = ("auth_done", 2) + self.set_state("auth_done", length=2) + return True def state_pre_connect(self): - if not self.read_buf_sufficient(4): - return False # Get the response if self.read_buf[0:1] != chr(0x05).encode(): self.close() @@ -91,41 +87,38 @@ class Socks5(Proxy): raise Socks5Error(9) # Get the bound address/port elif self.read_buf[3:4] == chr(0x01).encode(): - self.set_state("proxy_addr_1", 4) + self.set_state("proxy_addr_1", length=4, expectBytes=4) elif self.read_buf[3:4] == chr(0x03).encode(): - self.set_state("proxy_addr_2_1", 4) + self.set_state("proxy_addr_2_1", length=4, expectBytes=1) else: self.close() raise GeneralProxyError(1) + return True def state_proxy_addr_1(self): - if not self.read_buf_sufficient(4): - return False self.boundaddr = self.read_buf[0:4] - self.set_state("proxy_port", 4) + self.set_state("proxy_port", length=4, expectBytes=2) + return True def state_proxy_addr_2_1(self): - if not self.read_buf_sufficient(1): - return False self.address_length = ord(self.read_buf[0:1]) - self.set_state("proxy_addr_2_2", 1) + self.set_state("proxy_addr_2_2", length=1, expectBytes=self.address_length) + return True def state_proxy_addr_2_2(self): - if not self.read_buf_sufficient(self.address_length): - return False - self.boundaddr = self.read_buf - self.set_state("proxy_port", self.address_length) + self.boundaddr = self.read_buf[0:self.address_length] + self.set_state("proxy_port", length=self.address_length, expectBytes=2) + return True def state_proxy_port(self): - if not self.read_buf_sufficient(2): - return False self.boundport = struct.unpack(">H", self.read_buf[0:2])[0] self.__proxysockname = (self.boundaddr, self.boundport) if self.ipaddr is not None: self.__proxypeername = (socket.inet_ntoa(self.ipaddr), self.destination[1]) else: self.__proxypeername = (self.destination[0], self.destport) - self.set_state("proxy_handshake_done", 2) + self.set_state("proxy_handshake_done", length=2) + return True def proxy_sock_name(self): return socket.inet_ntoa(self.__proxysockname[0]) @@ -137,28 +130,29 @@ class Socks5Connection(Socks5): def state_auth_done(self): # Now we can request the actual connection - self.writeQueue.put(struct.pack('BBB', 0x05, 0x01, 0x00)) + self.append_write_buf(struct.pack('BBB', 0x05, 0x01, 0x00)) # If the given destination address is an IP address, we'll # use the IPv4 address request even if remote resolving was specified. try: self.ipaddr = socket.inet_aton(self.destination[0]) - self.writeQueue.put(chr(0x01).encode() + self.ipaddr) + self.append_write_buf(chr(0x01).encode() + self.ipaddr) except socket.error: # Well it's not an IP number, so it's probably a DNS name. if Proxy._remote_dns: # Resolve remotely self.ipaddr = None - self.writeQueue.put(chr(0x03).encode() + chr(len(self.destination[0])).encode() + self.destination[0]) + self.append_write_buf(chr(0x03).encode() + chr(len(self.destination[0])).encode() + self.destination[0]) else: # Resolve locally self.ipaddr = socket.inet_aton(socket.gethostbyname(self.destination[0])) - self.writeQueue.put(chr(0x01).encode() + self.ipaddr) - self.writeQueue.put(struct.pack(">H", self.destination[1])) - self.set_state("pre_connect", 0) + self.append_write_buf(chr(0x01).encode() + self.ipaddr) + self.append_write_buf(struct.pack(">H", self.destination[1])) + self.set_state("pre_connect", length=0, expectBytes=4) + return True def state_pre_connect(self): try: - Socks5.state_pre_connect(self) + return Socks5.state_pre_connect(self) except Socks5Error as e: self.handle_close(e.message) @@ -171,10 +165,11 @@ class Socks5Resolver(Socks5): def state_auth_done(self): # Now we can request the actual connection - self.writeQueue.put(struct.pack('BBB', 0x05, 0xF0, 0x00)) - self.writeQueue.put(chr(0x03).encode() + chr(len(self.host)).encode() + str(self.host)) - self.writeQueue.put(struct.pack(">H", self.port)) - self.set_state("pre_connect", 0) + self.append_write_buf(struct.pack('BBB', 0x05, 0xF0, 0x00)) + self.append_write_buf(chr(0x03).encode() + chr(len(self.host)).encode() + str(self.host)) + self.append_write_buf(struct.pack(">H", self.port)) + self.set_state("pre_connect", length=0, expectBytes=4) + return True def resolved(self): print "Resolved %s as %s" % (self.host, self.proxy_sock_name()) diff --git a/src/network/tcp.py b/src/network/tcp.py index c2052df1..8ef3d1e1 100644 --- a/src/network/tcp.py +++ b/src/network/tcp.py @@ -29,7 +29,7 @@ from network.tls import TLSDispatcher import addresses from bmconfigparser import BMConfigParser -from queues import invQueue, objectProcessorQueue, portCheckerQueue, UISignalQueue +from queues import invQueue, objectProcessorQueue, portCheckerQueue, UISignalQueue, receiveDataQueue import shared import state import protocol @@ -91,6 +91,12 @@ class TCPConnection(BMProto, TLSDispatcher): logger.debug("Skipping processing getdata due to missing object for %.2fs", self.skipUntil - time.time()) self.skipUntil = time.time() + delay + def state_connection_fully_established(self): + self.set_connection_fully_established() + self.set_state("bm_header") + self.bm_proto_reset() + return True + def set_connection_fully_established(self): if not self.isOutbound and not self.local: shared.clientHasReceivedIncomingConnections = True @@ -144,10 +150,37 @@ class TCPConnection(BMProto, TLSDispatcher): for peer, params in addrs[substream]: templist.append((substream, peer, params["lastseen"])) if len(templist) > 0: - self.writeQueue.put(BMProto.assembleAddr(templist)) + self.append_write_buf(BMProto.assembleAddr(templist)) def sendBigInv(self): - self.receiveQueue.put(("biginv", None)) + def sendChunk(): + if objectCount == 0: + return + logger.debug('Sending huge inv message with %i objects to just this one peer', objectCount) + self.append_write_buf(protocol.CreatePacket('inv', addresses.encodeVarint(objectCount) + payload)) + + # Select all hashes for objects in this stream. + bigInvList = {} + for stream in self.streams: + # may lock for a long time, but I think it's better than thousands of small locks + with self.objectsNewToThemLock: + for objHash in Inventory().unexpired_hashes_by_stream(stream): + bigInvList[objHash] = 0 + self.objectsNewToThem[objHash] = time.time() + objectCount = 0 + payload = b'' + # Now let us start appending all of these hashes together. They will be + # sent out in a big inv message to our new peer. + for hash, storedValue in bigInvList.items(): + payload += hash + objectCount += 1 + if objectCount >= BMProto.maxObjectCount: + self.sendChunk() + payload = b'' + objectCount = 0 + + # flush + sendChunk() def handle_connect(self): try: @@ -156,9 +189,10 @@ class TCPConnection(BMProto, TLSDispatcher): if e.errno in asyncore._DISCONNECTED: logger.debug("%s:%i: Connection failed: %s" % (self.destination.host, self.destination.port, str(e))) return - self.writeQueue.put(protocol.assembleVersionMessage(self.destination.host, self.destination.port, network.connectionpool.BMConnectionPool().streams, False)) + self.append_write_buf(protocol.assembleVersionMessage(self.destination.host, self.destination.port, network.connectionpool.BMConnectionPool().streams, False)) #print "%s:%i: Sending version" % (self.destination.host, self.destination.port) self.connectedAt = time.time() + receiveDataQueue.put(self) def handle_read(self): TLSDispatcher.handle_read(self) @@ -169,6 +203,7 @@ class TCPConnection(BMProto, TLSDispatcher): knownnodes.knownNodes[s][self.destination]["lastseen"] = time.time() except KeyError: pass + receiveDataQueue.put(self) def handle_write(self): TLSDispatcher.handle_write(self) @@ -187,10 +222,10 @@ class Socks5BMConnection(Socks5Connection, TCPConnection): def state_proxy_handshake_done(self): Socks5Connection.state_proxy_handshake_done(self) - self.writeQueue.put(protocol.assembleVersionMessage(self.destination.host, self.destination.port, \ + self.append_write_buf(protocol.assembleVersionMessage(self.destination.host, self.destination.port, \ network.connectionpool.BMConnectionPool().streams, False)) self.set_state("bm_header", expectBytes=protocol.Header.size) - return False + return True class Socks4aBMConnection(Socks4aConnection, TCPConnection): @@ -201,10 +236,10 @@ class Socks4aBMConnection(Socks4aConnection, TCPConnection): def state_proxy_handshake_done(self): Socks4aConnection.state_proxy_handshake_done(self) - self.writeQueue.put(protocol.assembleVersionMessage(self.destination.host, self.destination.port, \ + self.append_write_buf(protocol.assembleVersionMessage(self.destination.host, self.destination.port, \ network.connectionpool.BMConnectionPool().streams, False)) self.set_state("bm_header", expectBytes=protocol.Header.size) - return False + return True class TCPServer(AdvancedDispatcher): diff --git a/src/network/tls.py b/src/network/tls.py index 9dafaab2..69fc2c20 100644 --- a/src/network/tls.py +++ b/src/network/tls.py @@ -10,6 +10,7 @@ import sys from debug import logger from network.advanceddispatcher import AdvancedDispatcher import network.asyncore_pollchoose as asyncore +from queues import receiveDataQueue import paths import protocol @@ -58,14 +59,17 @@ class TLSDispatcher(AdvancedDispatcher): do_handshake_on_connect=False) self.sslSocket.setblocking(0) self.want_read = self.want_write = True - self.set_state("bm_header") + self.set_state("tls_handshake") + return False # if hasattr(self.socket, "context"): # self.socket.context.set_ecdh_curve("secp256k1") + def state_tls_handshake(self): + return False + def writable(self): try: - if self.tlsStarted and not self.tlsDone and not self.write_buf and self.writeQueue.empty(): - #print "tls writable, %r" % (self.want_write) + if self.tlsStarted and not self.tlsDone and not self.write_buf: return self.want_write return AdvancedDispatcher.writable(self) except AttributeError: @@ -73,9 +77,13 @@ class TLSDispatcher(AdvancedDispatcher): def readable(self): try: - if self.tlsStarted and not self.tlsDone and not self.write_buf and self.writeQueue.empty(): + # during TLS handshake, and after flushing write buffer, return status of last handshake attempt + if self.tlsStarted and not self.tlsDone and not self.write_buf: #print "tls readable, %r" % (self.want_read) return self.want_read + # prior to TLS handshake, receiveDataThread should emulate synchronous behaviour + elif not self.fullyEstablished and (self.expectBytes == 0 or not self.write_buf_empty()): + return False return AdvancedDispatcher.readable(self) except AttributeError: return AdvancedDispatcher.readable(self) @@ -83,11 +91,11 @@ class TLSDispatcher(AdvancedDispatcher): def handle_read(self): try: # wait for write buffer flush - if self.tlsStarted and not self.tlsDone and not self.write_buf and self.writeQueue.empty(): - #print "handshaking (read)" + if self.tlsStarted and not self.tlsDone and not self.write_buf: + #logger.debug("%s:%i TLS handshaking (read)", self.destination.host, self.destination.port) self.tls_handshake() else: - #print "not handshaking (read)" + #logger.debug("%s:%i Not TLS handshaking (read)", self.destination.host, self.destination.port) return AdvancedDispatcher.handle_read(self) except AttributeError: return AdvancedDispatcher.handle_read(self) @@ -104,14 +112,14 @@ class TLSDispatcher(AdvancedDispatcher): def handle_write(self): try: # wait for write buffer flush - if self.tlsStarted and not self.tlsDone and not self.write_buf and self.writeQueue.empty(): - #print "handshaking (write)" + if self.tlsStarted and not self.tlsDone and not self.write_buf: + #logger.debug("%s:%i TLS handshaking (write)", self.destination.host, self.destination.port) self.tls_handshake() else: - #print "not handshaking (write)" + #logger.debug("%s:%i Not TLS handshaking (write)", self.destination.host, self.destination.port) return AdvancedDispatcher.handle_write(self) except AttributeError: - return AdvancedDispatcher.handle_read(self) + return AdvancedDispatcher.handle_write(self) except ssl.SSLError as err: if err.errno == ssl.SSL_ERROR_WANT_WRITE: return 0 @@ -158,6 +166,8 @@ class TLSDispatcher(AdvancedDispatcher): self.del_channel() self.set_socket(self.sslSocket) self.tlsDone = True - self.set_state("bm_header") - self.set_connection_fully_established() + + self.bm_proto_reset() + self.set_state("connection_fully_established") + receiveDataQueue.put(self) return False diff --git a/src/network/udp.py b/src/network/udp.py index 6770e5a0..824c9bfa 100644 --- a/src/network/udp.py +++ b/src/network/udp.py @@ -9,7 +9,7 @@ from network.bmobject import BMObject, BMObjectInsufficientPOWError, BMObjectInv import network.asyncore_pollchoose as asyncore from network.objectracker import ObjectTracker -from queues import objectProcessorQueue, peerDiscoveryQueue, UISignalQueue +from queues import objectProcessorQueue, peerDiscoveryQueue, UISignalQueue, receiveDataQueue import state import protocol @@ -80,7 +80,7 @@ class UDPSocket(BMProto): addresses = self._decode_addr() # only allow peer discovery from private IPs in order to avoid attacks from random IPs on the internet if not self.local: - return + return True remoteport = False for i in addresses: seenTime, stream, services, ip, port = i @@ -93,7 +93,7 @@ class UDPSocket(BMProto): # if the address isn't local, interpret it as the hosts' own announcement remoteport = port if remoteport is False: - return + return True logger.debug("received peer discovery from %s:%i (port %i):", self.destination.host, self.destination.port, remoteport) if self.local: peerDiscoveryQueue.put(state.Peer(self.destination.host, remoteport)) @@ -118,7 +118,7 @@ class UDPSocket(BMProto): return def writable(self): - return not self.writeQueue.empty() + return self.write_buf def readable(self): return len(self.read_buf) < AdvancedDispatcher._buf_len @@ -139,18 +139,14 @@ class UDPSocket(BMProto): # overwrite the old buffer to avoid mixing data and so that self.local works correctly self.read_buf = recdata self.bm_proto_reset() - self.process() + receiveDataQueue.put(self) def handle_write(self): try: - data = self.writeQueue.get(False) - except Queue.Empty: - return - try: - retval = self.socket.sendto(data, ('', UDPSocket.port)) + retval = self.socket.sendto(self.write_buf, ('', UDPSocket.port)) except socket.error as e: logger.error("socket error on sendato: %s", str(e)) - self.writeQueue.task_done() + self.slice_write_buf(retval) if __name__ == "__main__": diff --git a/src/queues.py b/src/queues.py index 223c7c3b..f768c59f 100644 --- a/src/queues.py +++ b/src/queues.py @@ -12,5 +12,6 @@ invQueue = MultiQueue() addrQueue = MultiQueue() portCheckerQueue = Queue.Queue() peerDiscoveryQueue = Queue.Queue() +receiveDataQueue = Queue.Queue() apiAddressGeneratorReturnQueue = Queue.Queue( ) # The address generator thread uses this queue to get information back to the API thread. From de22e547c57be633cb32d51e93a6efe9c9e90293 Mon Sep 17 00:00:00 2001 From: Peter Surda Date: Thu, 6 Jul 2017 20:06:43 +0200 Subject: [PATCH 144/407] Remove buggy log message if prctl is missing - it's not that important that you need to be informed of it, and importing logging may cause cyclic dependencies/other problems --- src/helper_threading.py | 1 - 1 file changed, 1 deletion(-) diff --git a/src/helper_threading.py b/src/helper_threading.py index cd51557f..7ea4a12d 100644 --- a/src/helper_threading.py +++ b/src/helper_threading.py @@ -10,7 +10,6 @@ try: threading.Thread.__bootstrap_original__ = threading.Thread._Thread__bootstrap threading.Thread._Thread__bootstrap = _thread_name_hack except ImportError: - log('WARN: prctl module is not installed. You will not be able to see thread names') def set_thread_name(name): pass class StoppableThread(object): From a98b8690d3e5276f47299366b44f1faaad6319f3 Mon Sep 17 00:00:00 2001 From: Peter Surda Date: Fri, 7 Jul 2017 07:55:29 +0200 Subject: [PATCH 145/407] Asyncore fixes - fix broken loops - optimise I/O tests --- src/network/advanceddispatcher.py | 54 ++++++++++++++----------------- src/network/receivequeuethread.py | 6 +--- 2 files changed, 26 insertions(+), 34 deletions(-) diff --git a/src/network/advanceddispatcher.py b/src/network/advanceddispatcher.py index 57bd4f41..338e3bba 100644 --- a/src/network/advanceddispatcher.py +++ b/src/network/advanceddispatcher.py @@ -40,18 +40,14 @@ class AdvancedDispatcher(asyncore.dispatcher): def process(self): if not self.connected: - return - loop = 0 + return False while len(self.read_buf) >= self.expectBytes: - loop += 1 - if loop > 1000: - logger.error("Stuck at state %s, report this bug please", self.state) - break try: if getattr(self, "state_" + str(self.state))() is False: break except AttributeError: raise + return False def set_state(self, state, length=0, expectBytes=0): self.expectBytes = expectBytes @@ -59,39 +55,39 @@ class AdvancedDispatcher(asyncore.dispatcher): self.state = state def writable(self): + self.uploadChunk = AdvancedDispatcher._buf_len + if asyncore.maxUploadRate > 0: + self.uploadChunk = asyncore.uploadBucket + self.uploadChunk = min(self.uploadChunk, len(self.write_buf)) return asyncore.dispatcher.writable(self) and \ - (self.connecting or self.write_buf) + (self.connecting or self.uploadChunk > 0) def readable(self): + self.downloadChunk = AdvancedDispatcher._buf_len + if asyncore.maxDownloadRate > 0: + self.downloadChunk = asyncore.downloadBucket + try: + if self.expectBytes > 0 and not self.fullyEstablished: + self.downloadChunk = min(self.downloadChunk, self.expectBytes - len(self.read_buf)) + except AttributeError: + pass return asyncore.dispatcher.readable(self) and \ - (self.connecting or len(self.read_buf) < AdvancedDispatcher._buf_len) + (self.connecting or self.downloadChunk > 0) def handle_read(self): self.lastTx = time.time() - downloadBytes = AdvancedDispatcher._buf_len - if asyncore.maxDownloadRate > 0: - downloadBytes = asyncore.downloadBucket - if self.expectBytes > 0 and downloadBytes > self.expectBytes - len(self.read_buf): - downloadBytes = self.expectBytes - len(self.read_buf) - if downloadBytes > 0: - newData = self.recv(downloadBytes) - self.receivedBytes += len(newData) - asyncore.update_received(len(newData)) - with self.readLock: - self.read_buf += newData + newData = self.recv(self.downloadChunk) + self.receivedBytes += len(newData) + asyncore.update_received(len(newData)) + with self.readLock: + self.read_buf += newData def handle_write(self): self.lastTx = time.time() - bufSize = AdvancedDispatcher._buf_len - if asyncore.maxUploadRate > 0: - bufSize = asyncore.uploadBucket - if bufSize <= 0: - return - if self.write_buf: - written = self.send(self.write_buf[0:bufSize]) - asyncore.update_sent(written) - self.sentBytes += written - self.slice_write_buf(written) + written = self.send(self.write_buf[0:self.uploadChunk]) + asyncore.update_sent(written) + self.sentBytes += written + self.slice_write_buf(written) def handle_connect_event(self): try: diff --git a/src/network/receivequeuethread.py b/src/network/receivequeuethread.py index 120d15e2..f1e81a0d 100644 --- a/src/network/receivequeuethread.py +++ b/src/network/receivequeuethread.py @@ -34,11 +34,7 @@ class ReceiveQueueThread(threading.Thread, StoppableThread): # cycle as long as there is data # methods should return False if there isn't enough data, or the connection is to be aborted try: - while connection.process(): - pass + connection.process() except AttributeError: # missing command logger.error("Unknown state %s, ignoring", connection.state) - - def stopThread(self): - super(ReceiveQueueThread, self).stopThread() From 4f969088cfdd271d5c5a1a2f7614b4e6f627589d Mon Sep 17 00:00:00 2001 From: PyBitmessage Translations Date: Thu, 6 Jul 2017 23:39:57 +0200 Subject: [PATCH 146/407] Auto-updated language pl from transifex --- src/translations/bitmessage_pl.qm | Bin 89888 -> 90995 bytes src/translations/bitmessage_pl.ts | 49 ++++++++++++++++++++++-------- 2 files changed, 37 insertions(+), 12 deletions(-) diff --git a/src/translations/bitmessage_pl.qm b/src/translations/bitmessage_pl.qm index ab80193018c5ae2ea21ef7d311873aab5f66d209..4ecb0a11d0dffee1d30516e399739dab8ff4aab8 100644 GIT binary patch delta 2224 zcmYLKdr(y875{yA@7>3}3mZ{EFl?_6R%K*;f(nB21`9|;u|{gT>|S7zePnl$E~$Xk z(L`&7e6bsiG)|+_8Y3npkJ4!pjY(xX;>2;Bj-;A2(blnQw5CndR_VEGw12=k_k8F3 z{eI{1%^rMDy8C;ncYU9cw^tJ!zb zL?ATErpy@voBuCfO=qRsxKD5jhB4kxUscQayFzp72SVOwTY%tcp}dFe(}#tY<^Les zqdebW7baWy_YDP83!}nlq6^F#6#gQHfvbW+{82WTJi%c7iA17~@V5+(%*59XS;fbJ z<1xeB^KSr)w;8gnr@&I)H{=AmNXK^#`86t7jgw1afl!#oOU1HP<{hH#DXJ*HOH6)s z9dJ8K%zuX(n)i3HxaJ7(r!U2dk*|P8pV&D51+c{XqITR#&3r}Ns0;wN_KR&ZI{|4_ zj08yxcJK>gnf&-n@#KwJU`tDRhSZfg`=Ye9M*}NKke+?LoJuK>cIP^QeQTu`m&^jr z9+dXq4TDWf;v3h`kC|#@VbC)9mn68jg-hls zGgh400E|6lslpiW_G!zyF&S)GtIjwD--tUOJP!>{G>)im76zv3fiXZP|0a*Kzu%HNY)Kg$L#Uy55cwNCD8xDK{( zzP$gh{{@DJ<$;2K)1F_G2iM*Q-q;`yDKzC5ljRd|GvwXK@ifKu0 z2AKF7KW4FaPuZ!|{FXQm-BF$xJ3`}GqPU+a1WV{qc6Dwgo1*l-KT2B4l>O(6!KUt2 z4pIjY~A%yFL5+mUs#q# zPwNKnmkWfFK7L7d$OV(FpFDG(qGdcmX%ZIn@^-}`2w(Gl#VOkZiRMWp!}2qJfuhs@ z!0##k`P23!e)+_7dVB{G@7oi>9zSE7laU5kCfVlu+re@-*s_Q1V2cjh@+Q;sO4E41 z)j3s~X4_ImVcT=IJ)`uNHy^bfvR|Z!V6nYAav#j`5x;58F_nBwYg(T)n3>=n8#fzu z=tLAv2qAzDxcH|hCdTG2XGYa!rcHs7{w5X&{C>?H@do^==68ppLEViNCbJZ_G{zEG zYOEoJ&5oV7vhLF9s6;JPw4oi1Wc_dv10j>&LL6a4@PFv!%39Us@r1N+SPg{KmOwb- zcloq9_B*Q*n8YlbPzA4kjgM8N@Gn#!57^BC0M-gsynCD%l{_@5M&)beVeX8{-j;GTeInk%IJE zf}9an!&+NYR!9p5LOORi&=l!#g}!Y>?PKh=D6l7E2L+azt$2(b0c6pw~*7mcz1C@j6(L!Ig%VII2Vil;^OeodG^k4V%6JH#qBldP38yD+MXUW|(238U; zpd+5yLFkQyr?=4iAp4ObQb!mMHBh5T=R;QKqec$YMvJ@=A8nGWSyLk+mwRJ4KC^JU z+pX_ch^DHK+D+Ts{p~utA`#6Oj8I}g_4+;DOJX+YPuBM68Ew8_sjK5h6 zt4*PRPtBUAr_YNog{!HFpdJvlc{gfp-j+bX6W=JUO(&td`r_z_#1QwJ+9QVchqEo5 I$Ud$7A4joTYXATM delta 1308 zcmX9-YfzL`7=GSwzi+?&zHcwOTx8c+(PR}S7Q}YtwhOk3z{nahtWlzQ!IY9jvQZom zhBB`Ob0m}`uauXB%qhf))QrR*@FRXWuE97?WI2R7W@Qu5nfh_&ne(3aJm)>n^S+P2 zmyUiTHH0gZ0L%f|K52}0T9tU<6}~=`f%OYO1<{Q~PC@vkvD#_(uZ47RHz=nZ`qMgo zwh(e~A;*i2-3~#GY#ek7X&p?WVA|XqEZzDwC$9M)f4YX?o-&YkH1kWzkM-CzR3j2**g6$@Y793V_F-^0Jz~&H5PwWD@`dGf; z5at)Nje<)Mwy;*d%L!-Q!fUpbLi(LLVAFMBQ7y-cHer3%6OgZ#d36pUa)_1ae0Fa@ z2$@%b3=4&y#Y$jU&>8nhpxhePt#ih!@9Gl#9Y9C3E@|LAkn)bsZM+5wJD~HtS;OCt z(Rl+gpgbKji9W$QiRFvgdHS8AWhMVfYM~f;rWkmbAbLBwkn^sK{=jMA#|L6g-|xWc zjpFLC10Z!kEbVY{5x0u3Ywf^ji}=RuDnJUcVKG~sGA3Rfi36qBnOmxfpWQCi*Or1Z z?@GHqU&KofllG^%fJ4iqgQ;;q|52&saV5xcj}1!B_>>Q&`-_$U2`1?e-ASPQzVzpU z$v`aAPcGwW{wRH68`s6QnI-5Q)~qS|U2oR_%l-PpdBdP3Pg#lHv)VUHKN#dCW_0LB z_2t0vZ)I!44xs8Id7j!2l4Ut<)hu9Vtek(VAJ{V{2OFw^yL;u*IUD%bMY;Y+Cg8Wq zI~UtP^KZ!SyN7@mNp6Td0ZjbBp6G)@b`i^$UDnJqhLlI6!2Whap|6O0HpKSJi8PaS z%K??*l&PQcf-}7=!Z3Z>VoU&ET~~@HRPIYisSJDvta(SNdpZu1Dp`ra8LwPYTI+8B zH;yYuGkE&wU&{GoT$6QGN?)=a6cMfTjeQIX_poyYr&ZHbt%56wFVwJn?(&psHf(U^ zWmKr^*YYesQ_JqSfuX_wkQ}qsn&tOFNwV5#f}YbmeNjciVv@kF=YI0-%^N z?bfzY&JzfoF-dc(sSk~}w-4~>ROZ&!35lhw zRdWiok@aXU)xN-Nh~nkQO>B%uV~(&`V|ntlwdMx}Gx;c0n8zJvP}DI?VyqodqAbbf zTR@(<7I&`$G`GrWszystkich połączeń: - + Since startup: Od startu: - + Processed 0 person-to-person messages. Przetworzono 0 wiadomości zwykłych. - + Processed 0 public keys. Przetworzono 0 kluczy publicznych. - + Processed 0 broadcasts. Przetworzono 0 wiadomości przekazów. - + Inventory lookups per second: 0 Zapytań o elementy na sekundę: 0 - + Objects to be synced: Obiektów do zsynchronizowania: - + Stream # Strumień # @@ -1912,12 +1912,12 @@ Generowanie adresów 'losowych' jest wybrane domyślnie, jednak determ Zapytań o elementy na sekundę: %1 - + Up: 0 kB/s Wysyłanie: 0 kB/s - + Down: 0 kB/s Pobieranie: 0 kB/s @@ -1957,20 +1957,45 @@ Generowanie adresów 'losowych' jest wybrane domyślnie, jednak determ Użytkownik - + + IP address or hostname + IP lub nazwa hosta + + + Rating Ocena - + + PyBitmessage tracks the success rate of connection attempts to individual nodes. The rating ranges from -1 to 1 and affects the likelihood of selecting the node in the future + PyBitmessage rejestruje pomyślność prób połączeń z indywidualnymi węzłami. Ocena przyjmuje wartości od -1 do 1 i ma wpływ na prawdopodobieństwo wybrania węzła w przyszłości. + + + User agent Klient - + + Peer's self-reported software + Ogłaszana aplikacja kliencka + + + TLS TLS + + + Connection encryption + Szyfrowanie połączenia + + + + List of streams negotiated between you and the peer + Lista strumieni negocjowanych pomiędzy Tobą i użytkownikiem + newChanDialog From 6eb9e93575f53de1d6a76e46c2ba71b0622bcb27 Mon Sep 17 00:00:00 2001 From: PyBitmessage Translations Date: Fri, 7 Jul 2017 15:20:23 +0200 Subject: [PATCH 147/407] Auto-updated language ja from transifex --- src/translations/bitmessage_ja.qm | Bin 65615 -> 66390 bytes src/translations/bitmessage_ja.ts | 49 ++++++++++++++++++++++-------- 2 files changed, 37 insertions(+), 12 deletions(-) diff --git a/src/translations/bitmessage_ja.qm b/src/translations/bitmessage_ja.qm index d5116555b3041a779cd40a5be68969fc0bd41510..37e8086d9a717d80de0d10fe159843685ad6c397 100644 GIT binary patch delta 1979 zcmYLKc~BH*7Jt*zb9E2Qio0aRFg9)+H*7I}#09ScjRuSdh@dE0JO-u*hNGKIH!2}= zlojH!>fonkVu@OW#3hL&TWBc+NC0E-7}k4(29_9=fFN--No7}b-$2U!W9EIw``+)p z-|xMdf0VJ;D_HMuO)~)?62Nz~GSn8-x&`1b;Q+<|0?2*^;K$XKD{V}Zt8%j~WbjX* zJGBj9*a7-lE#8%ZAvF$dS7o!6X`5ErZDZ~vQ=KMtcCP}-o0>52hA~^S6H>S50Q9{B ztK4s(eFCdb!~#sw!E4HF4A}zd`t4|U(S4d|=GME^r?E1%_W)#5Zv0C$AbpSuir6;tu@U{wV z#DY%%N~*Nv%HIKOA=)T&J3vRbHd-zL_-0relPI8j3)Qi)Oy6@fj*U-lnZ#PwVM(_O z*x(P-0P0?3V@@KY-(|B45^Dhton&A2Jpza;Wi!U_1!(JF#ZTg)S=HLTvd_oDp`CjTX+Dq^5;BSy75-?^jc5@}^ z0{}g1xdVNV0lX|%7yBRV@H1TfD#R-NhHKza=Df??7oFJYt>d`%vU(KgFn6&6IaoZx zeVdNd?zVG7`w_0Em)9iJ0m$*ZsR)IQ$mTiiB^VFsR-U`90-W>m0p)R+ZU^0I3Ny?~ zngY<|rN>Ox(u>J_Vk2U1sN|PFtwntn@|ndiBW4}H;|(0_)+N06^Bb7UWB$OE1=ti} z{6W*7QK#elM||Ic?50WLq`yNXbOdnNphSN8!}fgcP) zeLp?H4+=RriAT&NbuG~OTx599^X7s@RRDKRo7J0h04D4<7o}+c-rQy0@ue3r%FTP{ zhTyPN&?+vL>3>MOIU9G|XTDi{1?T?<8o;}l?(4LWw=tg`qgA|}>zom2{5_`8a+hAi z)62){2rs>K>0aRQ@)r>Ng}^7)K!6KZEz_o8VEtvwOHv^=S&}8H!wS&*o@LHNoUij5 zT4lCRZtAy`$K!GP49o5tILv9wEH&0HfP{9-hd$))>I6Dujy4QTMjn1*X=x*=e=VT> zR5+LU{^kR&{V6}E1RJqAK#!l_Es!Ni%v9a7B#2U`r>MZcr0c(Wj)tD8ig_LuK@te0 znw%wrq=A%>bwnmNan-RR;8~F1MM;9g>GX(-BFG*gTUJ!bkuQ!$X*|*9(J5qz>PAtNgd(|6a7a!;%@&0MQS=zFyZ{L=>)l^X*US$Y11}#~^I&!4ip8Xv zTtm1I$Wc=L3??)Q-%V8nMa*+Wc*FwP;|EY=m%7p68Lg-OV8=>EvzH!OKe1FMnk1g+ zFxgH_`YhQ*BtOI3U0atDDdRWEhn2U7zh-?p!g?oh{4GQF+8Xe>34@zY6}42r;^L P6{>~5QuyH9+Nl2liq59A delta 1291 zcmXAodrXs87{;G|{l0$vF0@)NnKc$P(hV}Jk`)!lWdi|?6fd*NCOVlKM8zqN4V_#h z8t=9_m^mU|HXOlBz%Zwv6&#|qOTlS$17vvNG8|}}b55tQH}S7G=bZQQoZoZKt?hhs zKJOYX4FND2@Oj;1tfmX4z(=9L<_;h|4)`m_y}-(yi*s+WntNwMIJ*<*+X`K|md=hr zbk3mo!tJqeb#vV9R_YyEK+OXyt`PcHp<+Y z&9xeq0iH)(H=hNR{-jm+2tfbG%%io2)|F`|#8v{iCE7_hP61Z4)~+@HjRV^7b-5&9 zh&Iv@MClzYfRExjr?MnIe%AR?-nf*ma@Wm|Jdpxagz%APh~%A}e5|7!IDCdr^#2Xa z$m7$76#;d<{EA8&QS|Yvl?vc{AHU`;Ld0jXE1Ht#bcbej0goG4sxCaOD_Pg>q#^hFbq{r! zz{Xpm$(08<{}ZRkO~Co*V#KmHfu&k8sfE^Q+eD{p3-C>gxMK1u(ryq7k3<8udE)lj zqkx`4ai9HnU`nLu8u=|ylgOUxoZO#NSdwTnb*Jg4K7I%+FV)YFT0oxt#`cOfu4WJO zhz_}SrZl>Y6nBqcBlKefXJZg3TqmUr%E09qDa&yQh+iocJRcxk2`p7lJ`R^k3apJ*!_2^yXvzQTFqI!%}G=-NdqF&Hb?~$t)l8C(9a*aZMuemPQ zH_=nS5XMp^3wK+?wn!HF$|)tFfd0_sUUpm>%bKJydHyWLQA>r{Ze{LZIZ;nnKH2;p z6*eooHx*J`qqyo?so_hd_(m*Q{l0QYdWY!$pm_6%_O@w?_g)3Cp;7tuYX@*GP-)2_ zo_`!sS`!Zd7ASota;xgJ(ifCL&-kJ`$+-l`=u~6gwu5buLqgjk)lX-Z0Qb7ojcwC` zQTx@L6bH=*^wIQNyyIA-Y8!Vh(^wEs+x3f#`&;R2rX(9nEiFJ&gYkr)*!>d0Mg)Y5 qJ+Z{&NiZ9znpC|tpy4drez}Bwe#KLL(HpF(j`l6!s{i#bvHuTGySh>U diff --git a/src/translations/bitmessage_ja.ts b/src/translations/bitmessage_ja.ts index 3aa2723e..9733c02a 100644 --- a/src/translations/bitmessage_ja.ts +++ b/src/translations/bitmessage_ja.ts @@ -1841,37 +1841,37 @@ The 'Random Number' option is selected by default but deterministic ad 接続数: - + Since startup: 起動日時: - + Processed 0 person-to-person messages. 0 通の1対1のメッセージを処理しました。 - + Processed 0 public keys. 0 件の公開鍵を処理しました。 - + Processed 0 broadcasts. 0 件の配信を処理しました。 - + Inventory lookups per second: 0 毎秒のインベントリ検索: 0 - + Objects to be synced: 同期する必要のあるオブジェクト: - + Stream # ストリーム # @@ -1906,12 +1906,12 @@ The 'Random Number' option is selected by default but deterministic ad 毎秒のインベントリ検索: %1 - + Up: 0 kB/s アップ: 0 kB/秒 - + Down: 0 kB/s ダウン: 0 kB/秒 @@ -1951,20 +1951,45 @@ The 'Random Number' option is selected by default but deterministic ad ピア - + + IP address or hostname + IP アドレスまたはホスト名 + + + Rating 評価 - + + PyBitmessage tracks the success rate of connection attempts to individual nodes. The rating ranges from -1 to 1 and affects the likelihood of selecting the node in the future + PyBitmessage は、個々のノードへの接続試行の成功率を追跡します。 率は -1 から 1 の範囲で、将来ノードを選択する可能性に影響します + + + User agent ユーザーエージェント - + + Peer's self-reported software + ピアの自己報告ソフトウェア + + + TLS TLS + + + Connection encryption + 接続暗号化 + + + + List of streams negotiated between you and the peer + あなたとピアの間でネゴシエーションしたストリームのリスト + newChanDialog From 1aa45d6eacdfa769f566c302c9ee8ec695ae9684 Mon Sep 17 00:00:00 2001 From: PyBitmessage Translations Date: Thu, 6 Jul 2017 23:35:37 +0200 Subject: [PATCH 148/407] Auto-updated language eo from transifex --- src/translations/bitmessage_eo.qm | Bin 86675 -> 87724 bytes src/translations/bitmessage_eo.ts | 49 ++++++++++++++++++++++-------- 2 files changed, 37 insertions(+), 12 deletions(-) diff --git a/src/translations/bitmessage_eo.qm b/src/translations/bitmessage_eo.qm index 2f3af4ec956873f5fea32a91fa79c3f9b079d1c3..69c28969c621850702ee10caacc3114925c62cb1 100644 GIT binary patch delta 2193 zcmYjSdsGzX6~Es+c6MhLHZBnb58KfeHb8cH2#65y6-5*X(TIHensbV$t)V$!=}B{k7ocJO9g0r7|6=Kz3bALsWZKu zY01TI;NSZxki`jL8DR0<2O!ie2U4Eidp3n7?7H5uG$uEL@}#CHV;t6OeIHZ56tiO? zsM*s5Bp+aXa2wjKu;J}efb0a=q!(e%r(v^Y585iapA<0OAJQS>VlrCkIPo&h!!(6) zId6@SA0%@Cp$%ki)@dMVhiN6_VhaCBLyVW1s-^w7TXdG5Wg3!x%@lu*tu*gv%DU0E zJi;_C{tu7>4=rI`jN=HcWlQJfon%JieLzSLbB)sh#*VYxQ&{1hI@bO?4`c~X-)2)Y z344n5EPE5+m%G^P%WolFqinwYJP>;)TcB;nLgLtxDjEHsraV{56qpdn#ym_kf->a-3pKeJNezeD#Kp3@Glg3 z0rpPuFE307_|0to;J#g%x(cLh53RHm)a9SCjMZSNc}~j>iwf|{1;N?V z1z>z4ED$dN++HIT`P=~AnL_0V?w=VFYI-^Wu2c$2P7sLqh_LguMF0;S6`rj~0rm}ycymhX7)o-@|+-$W~z#V|Wo3@R2MAYeHn_hJRV9Wcq9pBvsm{#d>Ybs7G zPeWFh7&mDfntl~XoSW{qra8HlqST7a!6h-F64kx0fDT!`UGqN`8~x~2yg_XG9Eaqy z15ry7hsEuizXIa^LOl4_TL2#&76(fwal(tllj{-ec(Qm}LYaI2D4x57lg_Z>`R~D&2kqhVejvFQ?Z%a20Qs`LVjQ9T(jgtb5yNI?)Y0Ts}?U%e_ z!i;$9>?$0qYjhY-lbYzXq~^}fjQ{4*A0c>k{B2h}kQqO5WThqpSg$#9)ixlWPaXLe zT|hGP9mR9-emQ2*e!F+xtp>-A6?n{bI({~amwBt;IO-a~uiaJ0@uAy5=48=vdx22s z$2l$EM4$hA#mV-#^qZwFSPdGepg=Pi-~*Z1AP517;MxzG6$b_^_?x**Q&pwj2x_XV zsP&PKu(|AOmB_5V*EsU}+FU4yYVbtEBA8Ccv@-Ze2%imjs$=?KbR{F42BH5;o>*Qj z`~3chqU*92ksCGLP<`)y%cdAQ7HZ033e}EJ6U7wnE*Uzu z+l*%>^RecdqwizFdLWFk8b%@|eRDZ7v&$3#6-I`r z!#7ynu{3Cea6EhsiflxD_04)z0==!i-W-|;iexHR|Gy#i8HN%H8|bIWLDe5@5Bl4D zEwZZl72PX8*7&$$Mg`RdG-`vQ%Yld%l07-5e@;|mUm$=39|LL$HY+W`Mosfa2SjNx auL8!Jb6}=Ki0C@dX0+WE^c9hOTlQb47hebf delta 1299 zcmX9;dr;I>7(Ms*+~0%UT)-4%alndfknCfDIaaRE5!h^vnBai;?S~*4F_7wbb{2%} z%>l`o7-)&)F}O6)W#w#>s7Sm{>vO!Z;T*gA!2j4)2=C4UkW>@${Tmr>$J}pRKuRd) z7dhFT0W6SqGW5~?#K9$`(IByK)>8VA*f?V&jpQumzT0F}ga8!ICDF-eL1G%Io3n6s zLJK(?*V{pB*ed-e+QKa#zLrbBSq7}!&*hdfTs4zhG36;pS_aMJEu40S`gm7F$_1{| zxEOeGgu5dY0bTXHULk^HhS3&2ZY%~b@<~~Zz~|ffl=jn1rcU0jUj#8!@QyVljKD}f z(-Xz&12jZ%aVcA=TbMe%|CwM~#GaDaFAS}p2mDzlWHvJ?$G$IQdFp^)&I;3lPXOO> zVM*vd5NN`(MjMlJKv=0Y0C)O?Rj+LX#II?$FjduCh4T;MS^IC)E|w%js$zNRGLUh5 z#T{SeGVX=qZl?{{+b@1RDIT~yOsxL92qd(Lc8S)6M2~nccP0?uAwJ_z0RC%YPxcTX zhL?sEurkMOX?86W#yE{8Nfz_C%hHbbN&xR1>Cp6UkgO`|lN?J@uS#8BMlmH?dL*p| zKDi{DE4KjaugVkDHsHBWb}k+Rl#Z3%9c;ZdD0?e612^3AvQ$4v!Wp^z$YdbnguHFW za1gs+{>c72ke4r44m}R^T%%7VFXt4gTeg|U%{NSZ{0P{-)iB#NhnaSO?v|6uNZKNM z)ZRE{#Ah#^yidao(W7Qy5NPO8<_)UMm!Fj)&m~~tIi;+30O&8KKEq2N!<3rxtH6bA z%Fzr~e|L#;`WO>s@ku3^76GCvO7O*D5XC`H8LVdHtJ+#7CECm` z$2KQ0V5UCB!i_GYn-zAUt8UL?RwrtQl<`dcXWErgKXAHEyYfc^P{nK4t2`j#L9L_s z5%YSc);VoIV7;V0Ff+IA_iGQL3K(%wPw_4U_Biw@iFVL+YJ5U$o&HW<4T$Z!zWzo! z5Y?_1&m+Leq57^?_DB5lslIPAlP+@>ZB<>|cqbJ#tD01y-`Ld7QhbrxwYRz0JX)h! zxn4hQ)oiLM%4itoVZ-v6zF@oI#WYS|ofhUa{xxef%hDp_fW-(Bvc{Ag7XcXVn9^3i z3le|JWWQno8Ix^FAHmY1S!t_oi|JcuDx1o-QDNHC$)dSIHl46^us^HYR397wdDTV3 wLmcw>A~tĈiuj konektoj: - + Since startup: Ekde starto: - + Processed 0 person-to-person messages. Pritraktis 0 inter-personajn mesaĝojn. - + Processed 0 public keys. Pritraktis 0 publikajn ŝlosilojn. - + Processed 0 broadcasts. Pritraktis 0 elsendojn. - + Inventory lookups per second: 0 Petoj pri inventaro en sekundo: 0 - + Objects to be synced: Samtempigotaj eroj: - + Stream # Fluo # @@ -1905,12 +1905,12 @@ La ‘hazardnombra’ adreso estas antaŭagordita, sed antaŭkalkuleblaj adresoj Petoj pri inventaro en sekundo: %1 - + Up: 0 kB/s Alŝuto: 0 kB/s - + Down: 0 kB/s Elŝuto: 0 kB/s @@ -1950,20 +1950,45 @@ La ‘hazardnombra’ adreso estas antaŭagordita, sed antaŭkalkuleblaj adresoj Samtavolano - + + IP address or hostname + IP-adreso aŭ gastiga nomo + + + Rating Takso - + + PyBitmessage tracks the success rate of connection attempts to individual nodes. The rating ranges from -1 to 1 and affects the likelihood of selecting the node in the future + PyBitmessage registras frekvencon de sukcesaj konekt-provoj al aliaj nodoj. Takso varias de -1 ĝis 1 kaj influas probablon por elekti nodon estontece. + + + User agent Klienta aplikaĵo - + + Peer's self-reported software + Anoncata klienta aplikaĵo + + + TLS TLS + + + Connection encryption + Konekta ĉifrado + + + + List of streams negotiated between you and the peer + Listo de interŝanĝataj fluoj inter vi kaj la samtavolano + newChanDialog From 5ae1b6d865165ebdbbb39db6ff28d51992ae108e Mon Sep 17 00:00:00 2001 From: Peter Surda Date: Sat, 8 Jul 2017 06:52:17 +0200 Subject: [PATCH 149/407] Asyncore update: remove obsolete files --- src/network/downloadqueue.py | 12 ------- src/network/uploadqueue.py | 70 ------------------------------------ 2 files changed, 82 deletions(-) delete mode 100644 src/network/downloadqueue.py delete mode 100644 src/network/uploadqueue.py diff --git a/src/network/downloadqueue.py b/src/network/downloadqueue.py deleted file mode 100644 index 3789fb19..00000000 --- a/src/network/downloadqueue.py +++ /dev/null @@ -1,12 +0,0 @@ -#import collections -from threading import current_thread, enumerate as threadingEnumerate, RLock -import Queue -import time - -#from helper_sql import * -from singleton import Singleton - -@Singleton -class DownloadQueue(Queue.Queue): - # keep a track of objects that have been advertised to us but we haven't downloaded them yet - maxWait = 300 diff --git a/src/network/uploadqueue.py b/src/network/uploadqueue.py deleted file mode 100644 index 5d699e96..00000000 --- a/src/network/uploadqueue.py +++ /dev/null @@ -1,70 +0,0 @@ -from collections import namedtuple -import Queue -import random -from threading import current_thread, enumerate as threadingEnumerate, RLock -import time - -#from helper_sql import * -from singleton import Singleton - -UploadElem = namedtuple("UploadElem", "stream identifier") - -class UploadQueueDeadlineException(Exception): - pass - - -class UploadQueue(object): - queueCount = 10 - - def __init__(self): - self.queue = [] - self.lastGet = 0 - self.getIterator = 0 - for i in range(UploadQueue.queueCount): - self.queue.append([]) - - def put(self, item): - self.queue[random.randrange(0, UploadQueue.queueCount)].append(item) - - def get(self): - i = UploadQueue.queueCount - retval = [] - while self.lastGet < time.time() - 1 and i > 0: - if len(self.queue) > 0: - retval.extend(self.queue[self.getIterator]) - self.queue[self.getIterator] = [] - self.lastGet += 1 - # only process each queue once - i -= 1 - self.getIterator = (self.getIterator + 1) % UploadQueue.queueCount - if self.lastGet < time.time() - 1: - self.lastGet = time.time() - return retval - - def streamElems(self, stream): - retval = {} - for q in self.queue: - for elem in q: - if elem.stream == stream: - retval[elem.identifier] = True - return retval - - def len(self): - retval = 0 - for i in range(UploadQueue.queueCount): - retval += len(self.queue[i]) - return retval - - def stop(self): - for i in range(UploadQueue.queueCount): - self.queue[i] = [] - - -@Singleton -class AddrUploadQueue(UploadQueue): - pass - - -@Singleton -class ObjUploadQueue(UploadQueue): - pass From 0f3a69adf42804d92946fc72f3eea65f934e8a8b Mon Sep 17 00:00:00 2001 From: Peter Surda Date: Sat, 8 Jul 2017 06:53:20 +0200 Subject: [PATCH 150/407] Asyncore update: remove references to deleted files --- src/network/bmproto.py | 1 - src/network/objectracker.py | 2 -- src/network/tcp.py | 2 -- 3 files changed, 5 deletions(-) diff --git a/src/network/bmproto.py b/src/network/bmproto.py index e00d6d0a..837c4ce8 100644 --- a/src/network/bmproto.py +++ b/src/network/bmproto.py @@ -15,7 +15,6 @@ import knownnodes from network.advanceddispatcher import AdvancedDispatcher from network.bmobject import BMObject, BMObjectInsufficientPOWError, BMObjectInvalidDataError, BMObjectExpiredError, BMObjectUnwantedStreamError, BMObjectInvalidError, BMObjectAlreadyHaveError import network.connectionpool -from network.downloadqueue import DownloadQueue from network.node import Node from network.objectracker import ObjectTracker from network.proxy import Proxy, ProxyError, GeneralProxyError diff --git a/src/network/objectracker.py b/src/network/objectracker.py index a7295c21..4541ea76 100644 --- a/src/network/objectracker.py +++ b/src/network/objectracker.py @@ -4,8 +4,6 @@ from threading import RLock from debug import logger from inventory import Inventory -from network.downloadqueue import DownloadQueue -from network.uploadqueue import UploadQueue haveBloom = False diff --git a/src/network/tcp.py b/src/network/tcp.py index 8ef3d1e1..1a4a906c 100644 --- a/src/network/tcp.py +++ b/src/network/tcp.py @@ -17,14 +17,12 @@ from network.advanceddispatcher import AdvancedDispatcher from network.bmproto import BMProtoError, BMProtoInsufficientDataError, BMProtoExcessiveDataError, BMProto from network.bmobject import BMObject, BMObjectInsufficientPOWError, BMObjectInvalidDataError, BMObjectExpiredError, BMObjectUnwantedStreamError, BMObjectInvalidError, BMObjectAlreadyHaveError import network.connectionpool -from network.downloadqueue import DownloadQueue from network.node import Node import network.asyncore_pollchoose as asyncore from network.proxy import Proxy, ProxyError, GeneralProxyError from network.objectracker import ObjectTracker from network.socks5 import Socks5Connection, Socks5Resolver, Socks5AuthError, Socks5Error from network.socks4a import Socks4aConnection, Socks4aResolver, Socks4aError -from network.uploadqueue import UploadQueue, UploadElem, AddrUploadQueue, ObjUploadQueue from network.tls import TLSDispatcher import addresses From 2d7d9c2f929a230bd9af18721aa52ba902d7e0be Mon Sep 17 00:00:00 2001 From: Peter Surda Date: Sat, 8 Jul 2017 06:54:25 +0200 Subject: [PATCH 151/407] Asyncore update - request downloads in bigger chunks - don't put whole objects into the receiveDataQueue --- src/network/downloadthread.py | 4 ++-- src/network/receivequeuethread.py | 26 +++++++++++++++++++++++--- src/network/tcp.py | 4 ++-- src/network/tls.py | 2 +- src/network/udp.py | 2 +- 5 files changed, 29 insertions(+), 9 deletions(-) diff --git a/src/network/downloadthread.py b/src/network/downloadthread.py index c42d7e1c..d1c7b0f4 100644 --- a/src/network/downloadthread.py +++ b/src/network/downloadthread.py @@ -11,8 +11,8 @@ from network.connectionpool import BMConnectionPool import protocol class DownloadThread(threading.Thread, StoppableThread): - maxPending = 50 - requestChunk = 100 + maxPending = 200 + requestChunk = 1000 requestTimeout = 60 cleanInterval = 60 requestExpires = 600 diff --git a/src/network/receivequeuethread.py b/src/network/receivequeuethread.py index f1e81a0d..5e707398 100644 --- a/src/network/receivequeuethread.py +++ b/src/network/receivequeuethread.py @@ -24,17 +24,37 @@ class ReceiveQueueThread(threading.Thread, StoppableThread): def run(self): while not self._stopped and state.shutdown == 0: try: - connection = receiveDataQueue.get(block=True, timeout=1) + dest = receiveDataQueue.get(block=True, timeout=1) receiveDataQueue.task_done() except Queue.Empty: continue if self._stopped: break + # cycle as long as there is data # methods should return False if there isn't enough data, or the connection is to be aborted + + # state_* methods should return False if there isn't enough data, + # or the connection is to be aborted + try: - connection.process() + BMConnectionPool().inboundConnections[dest].process() + except KeyError: + pass + except AttributeError: + logger.error("Unknown state %s, ignoring", connection.state) + + try: + BMConnectionPool().outboundConnections[dest].process() + except KeyError: + pass + except AttributeError: + logger.error("Unknown state %s, ignoring", connection.state) + + try: + BMConnectionPool().udpSockets[dest].process() + except KeyError: + pass except AttributeError: - # missing command logger.error("Unknown state %s, ignoring", connection.state) diff --git a/src/network/tcp.py b/src/network/tcp.py index 1a4a906c..75ddea1c 100644 --- a/src/network/tcp.py +++ b/src/network/tcp.py @@ -190,7 +190,7 @@ class TCPConnection(BMProto, TLSDispatcher): self.append_write_buf(protocol.assembleVersionMessage(self.destination.host, self.destination.port, network.connectionpool.BMConnectionPool().streams, False)) #print "%s:%i: Sending version" % (self.destination.host, self.destination.port) self.connectedAt = time.time() - receiveDataQueue.put(self) + receiveDataQueue.put(self.destination) def handle_read(self): TLSDispatcher.handle_read(self) @@ -201,7 +201,7 @@ class TCPConnection(BMProto, TLSDispatcher): knownnodes.knownNodes[s][self.destination]["lastseen"] = time.time() except KeyError: pass - receiveDataQueue.put(self) + receiveDataQueue.put(self.destination) def handle_write(self): TLSDispatcher.handle_write(self) diff --git a/src/network/tls.py b/src/network/tls.py index 69fc2c20..379dae99 100644 --- a/src/network/tls.py +++ b/src/network/tls.py @@ -169,5 +169,5 @@ class TLSDispatcher(AdvancedDispatcher): self.bm_proto_reset() self.set_state("connection_fully_established") - receiveDataQueue.put(self) + receiveDataQueue.put(self.destination) return False diff --git a/src/network/udp.py b/src/network/udp.py index 824c9bfa..5c19fa69 100644 --- a/src/network/udp.py +++ b/src/network/udp.py @@ -139,7 +139,7 @@ class UDPSocket(BMProto): # overwrite the old buffer to avoid mixing data and so that self.local works correctly self.read_buf = recdata self.bm_proto_reset() - receiveDataQueue.put(self) + receiveDataQueue.put(self.destination) def handle_write(self): try: From 2df9598774dfff1782db9804ae79e7ace8425f2e Mon Sep 17 00:00:00 2001 From: Peter Surda Date: Sat, 8 Jul 2017 07:33:29 +0200 Subject: [PATCH 152/407] Asyncore update: Fix incoming connections - dereferencing wasn't done correctly for incoming connections --- src/network/receivequeuethread.py | 19 +++++++++---------- 1 file changed, 9 insertions(+), 10 deletions(-) diff --git a/src/network/receivequeuethread.py b/src/network/receivequeuethread.py index 5e707398..6a2ebb65 100644 --- a/src/network/receivequeuethread.py +++ b/src/network/receivequeuethread.py @@ -35,26 +35,25 @@ class ReceiveQueueThread(threading.Thread, StoppableThread): # cycle as long as there is data # methods should return False if there isn't enough data, or the connection is to be aborted - # state_* methods should return False if there isn't enough data, - # or the connection is to be aborted + # state_* methods should return False if there isn't enough data, + # or the connection is to be aborted try: BMConnectionPool().inboundConnections[dest].process() except KeyError: - pass + try: + BMConnectionPool().inboundConnections[dest.host].process() + except KeyError: + pass except AttributeError: - logger.error("Unknown state %s, ignoring", connection.state) + pass try: BMConnectionPool().outboundConnections[dest].process() - except KeyError: + except (KeyError, AttributeError): pass - except AttributeError: - logger.error("Unknown state %s, ignoring", connection.state) try: BMConnectionPool().udpSockets[dest].process() - except KeyError: + except (KeyError, AttributeError): pass - except AttributeError: - logger.error("Unknown state %s, ignoring", connection.state) From 4fce01e34a5edde768ba643175b403fd7925000b Mon Sep 17 00:00:00 2001 From: Peter Surda Date: Sat, 8 Jul 2017 18:02:47 +0200 Subject: [PATCH 153/407] Less data transferred in invThread and addrThread --- src/network/addrthread.py | 4 ++++ src/network/bmproto.py | 4 ++-- src/network/connectionpool.py | 11 +++++++++++ src/network/invthread.py | 6 +++++- src/network/receivequeuethread.py | 19 +++---------------- 5 files changed, 25 insertions(+), 19 deletions(-) diff --git a/src/network/addrthread.py b/src/network/addrthread.py index a6c401ab..e0b31b6e 100644 --- a/src/network/addrthread.py +++ b/src/network/addrthread.py @@ -21,8 +21,12 @@ class AddrThread(threading.Thread, StoppableThread): try: data = addrQueue.get(False) chunk.append((data[0], data[1])) + if len(data) > 2: + source = BMConnectionPool().getConnectionByAddr(data[2]) except Queue.Empty: break + except KeyError: + continue #finish diff --git a/src/network/bmproto.py b/src/network/bmproto.py index 837c4ce8..78149726 100644 --- a/src/network/bmproto.py +++ b/src/network/bmproto.py @@ -290,7 +290,7 @@ class BMProto(AdvancedDispatcher, ObjectTracker): Inventory()[self.object.inventoryHash] = ( self.object.objectType, self.object.streamNumber, self.payload[objectOffset:], self.object.expiresTime, self.object.tag) - invQueue.put((self.object.streamNumber, self.object.inventoryHash, self)) + invQueue.put((self.object.streamNumber, self.object.inventoryHash, self.destination)) return True def _decode_addr(self): @@ -317,7 +317,7 @@ class BMProto(AdvancedDispatcher, ObjectTracker): "rating": 0, "self": False, } - addrQueue.put((stream, peer, self)) + addrQueue.put((stream, peer, self.destination)) return True def bm_command_portcheck(self): diff --git a/src/network/connectionpool.py b/src/network/connectionpool.py index 4c2b4c6c..ba5481da 100644 --- a/src/network/connectionpool.py +++ b/src/network/connectionpool.py @@ -54,6 +54,17 @@ class BMConnectionPool(object): def connectToStream(self, streamNumber): self.streams.append(streamNumber) + def getConnectionByAddr(self, addr): + if addr in self.inboundConnections: + return self.inboundConnections[addr] + if addr.host in self.inboundConnections: + return self.inboundConnections[addr.host] + if addr in self.outboundConnections: + return self.outboundConnections[addr] + if addr in self.udpSockets: + return self.udpSockets[addr] + raise KeyError + def addConnection(self, connection): if isinstance(connection, network.udp.UDPSocket): return diff --git a/src/network/invthread.py b/src/network/invthread.py index e5ab890a..0cc689b4 100644 --- a/src/network/invthread.py +++ b/src/network/invthread.py @@ -23,10 +23,14 @@ class InvThread(threading.Thread, StoppableThread): if len(data) == 2: BMConnectionPool().handleReceivedObject(data[0], data[1]) else: - BMConnectionPool().handleReceivedObject(data[0], data[1], data[2]) + source = BMConnectionPool().getConnectionByAddr(data[2]) + BMConnectionPool().handleReceivedObject(data[0], data[1], source) chunk.append((data[0], data[1])) except Queue.Empty: break + # connection not found, handle it as if generated locally + except KeyError: + BMConnectionPool().handleReceivedObject(data[0], data[1]) if chunk: for connection in BMConnectionPool().inboundConnections.values() + \ diff --git a/src/network/receivequeuethread.py b/src/network/receivequeuethread.py index 6a2ebb65..b6810a3c 100644 --- a/src/network/receivequeuethread.py +++ b/src/network/receivequeuethread.py @@ -39,21 +39,8 @@ class ReceiveQueueThread(threading.Thread, StoppableThread): # or the connection is to be aborted try: - BMConnectionPool().inboundConnections[dest].process() - except KeyError: - try: - BMConnectionPool().inboundConnections[dest.host].process() - except KeyError: - pass - except AttributeError: - pass - - try: - BMConnectionPool().outboundConnections[dest].process() - except (KeyError, AttributeError): - pass - - try: - BMConnectionPool().udpSockets[dest].process() + BMConnectionPool().getConnectionByAddr(dest).process() + # KeyError = connection object not found + # AttributeError = state isn't implemented except (KeyError, AttributeError): pass From f088e0ae21916c05deab43d2cdac12ec40fcaf22 Mon Sep 17 00:00:00 2001 From: Peter Surda Date: Mon, 10 Jul 2017 07:05:50 +0200 Subject: [PATCH 154/407] Change thread names - not needed to have "Thread" in the name of a thread --- src/network/addrthread.py | 4 ++-- src/network/announcethread.py | 4 ++-- src/network/downloadthread.py | 4 ++-- src/network/invthread.py | 4 ++-- src/network/networkthread.py | 4 ++-- 5 files changed, 10 insertions(+), 10 deletions(-) diff --git a/src/network/addrthread.py b/src/network/addrthread.py index e0b31b6e..8c78894f 100644 --- a/src/network/addrthread.py +++ b/src/network/addrthread.py @@ -10,9 +10,9 @@ import state class AddrThread(threading.Thread, StoppableThread): def __init__(self): - threading.Thread.__init__(self, name="AddrThread") + threading.Thread.__init__(self, name="AddrBroadcaster") self.initStop() - self.name = "AddrThread" + self.name = "AddrBroadcaster" def run(self): while not state.shutdown: diff --git a/src/network/announcethread.py b/src/network/announcethread.py index 354cfaa8..26d9f9cf 100644 --- a/src/network/announcethread.py +++ b/src/network/announcethread.py @@ -11,9 +11,9 @@ import state class AnnounceThread(threading.Thread, StoppableThread): def __init__(self): - threading.Thread.__init__(self, name="AnnounceThread") + threading.Thread.__init__(self, name="Announcer") self.initStop() - self.name = "AnnounceThread" + self.name = "Announcer" logger.info("init announce thread") def run(self): diff --git a/src/network/downloadthread.py b/src/network/downloadthread.py index d1c7b0f4..34f23eed 100644 --- a/src/network/downloadthread.py +++ b/src/network/downloadthread.py @@ -18,9 +18,9 @@ class DownloadThread(threading.Thread, StoppableThread): requestExpires = 600 def __init__(self): - threading.Thread.__init__(self, name="DownloadThread") + threading.Thread.__init__(self, name="Downloader") self.initStop() - self.name = "DownloadThread" + self.name = "Downloader" logger.info("init download thread") self.pending = {} self.lastCleaned = time.time() diff --git a/src/network/invthread.py b/src/network/invthread.py index 0cc689b4..d680ea13 100644 --- a/src/network/invthread.py +++ b/src/network/invthread.py @@ -10,9 +10,9 @@ import state class InvThread(threading.Thread, StoppableThread): def __init__(self): - threading.Thread.__init__(self, name="InvThread") + threading.Thread.__init__(self, name="InvBroadcaster") self.initStop() - self.name = "InvThread" + self.name = "InvBroadcaster" def run(self): while not state.shutdown: diff --git a/src/network/networkthread.py b/src/network/networkthread.py index a4a23103..25e9f547 100644 --- a/src/network/networkthread.py +++ b/src/network/networkthread.py @@ -9,9 +9,9 @@ import state class BMNetworkThread(threading.Thread, StoppableThread): def __init__(self): - threading.Thread.__init__(self, name="AsyncoreThread") + threading.Thread.__init__(self, name="Asyncore") self.initStop() - self.name = "AsyncoreThread" + self.name = "Asyncore" logger.info("init asyncore thread") def run(self): From bdf61489aebdcad5b5238602727894fb8801fe2e Mon Sep 17 00:00:00 2001 From: Peter Surda Date: Mon, 10 Jul 2017 07:08:10 +0200 Subject: [PATCH 155/407] Allow multiple ReceiveQueue threads - defaults to 3 --- src/bitmessagemain.py | 7 ++++--- src/bmconfigparser.py | 3 +++ src/helper_threading.py | 15 +++++++++++++++ src/network/advanceddispatcher.py | 7 +++++-- src/network/receivequeuethread.py | 10 +++++----- 5 files changed, 32 insertions(+), 10 deletions(-) diff --git a/src/bitmessagemain.py b/src/bitmessagemain.py index b142676b..3e0a1c84 100755 --- a/src/bitmessagemain.py +++ b/src/bitmessagemain.py @@ -267,9 +267,10 @@ class Main: asyncoreThread = BMNetworkThread() asyncoreThread.daemon = True asyncoreThread.start() - receiveQueueThread = ReceiveQueueThread() - receiveQueueThread.daemon = True - receiveQueueThread.start() + for i in range(BMConfigParser().getint("threads", "receive")): + receiveQueueThread = ReceiveQueueThread(i) + receiveQueueThread.daemon = True + receiveQueueThread.start() announceThread = AnnounceThread() announceThread.daemon = True announceThread.start() diff --git a/src/bmconfigparser.py b/src/bmconfigparser.py index 913df9c0..094cd73d 100644 --- a/src/bmconfigparser.py +++ b/src/bmconfigparser.py @@ -15,6 +15,9 @@ BMConfigDefaults = { "maxtotalconnections": 200, "maxuploadrate": 0, }, + "threads": { + "receive": 3, + }, "network": { "asyncore": True, "bind": None, diff --git a/src/helper_threading.py b/src/helper_threading.py index 7ea4a12d..56c5b8b2 100644 --- a/src/helper_threading.py +++ b/src/helper_threading.py @@ -1,4 +1,6 @@ +from contextlib import contextmanager import threading + try: import prctl def set_thread_name(name): prctl.set_name(name) @@ -20,3 +22,16 @@ class StoppableThread(object): def stopThread(self): self._stopped = True self.stop.set() + +class BusyError(threading.ThreadError): + pass + +@contextmanager +def nonBlocking(lock): + locked = lock.acquire(False) + if not locked: + raise BusyError + try: + yield + finally: + lock.release() diff --git a/src/network/advanceddispatcher.py b/src/network/advanceddispatcher.py index 338e3bba..dbe65e39 100644 --- a/src/network/advanceddispatcher.py +++ b/src/network/advanceddispatcher.py @@ -6,6 +6,7 @@ import time import asyncore_pollchoose as asyncore from debug import logger +from helper_threading import BusyError, nonBlocking class AdvancedDispatcher(asyncore.dispatcher): _buf_len = 2097152 # 2MB @@ -22,6 +23,7 @@ class AdvancedDispatcher(asyncore.dispatcher): self.expectBytes = 0 self.readLock = threading.RLock() self.writeLock = threading.RLock() + self.processingLock = threading.RLock() def append_write_buf(self, data): if data: @@ -43,8 +45,9 @@ class AdvancedDispatcher(asyncore.dispatcher): return False while len(self.read_buf) >= self.expectBytes: try: - if getattr(self, "state_" + str(self.state))() is False: - break + with nonBlocking(self.processingLock): + if getattr(self, "state_" + str(self.state))() is False: + break except AttributeError: raise return False diff --git a/src/network/receivequeuethread.py b/src/network/receivequeuethread.py index b6810a3c..a899851f 100644 --- a/src/network/receivequeuethread.py +++ b/src/network/receivequeuethread.py @@ -15,17 +15,16 @@ import protocol import state class ReceiveQueueThread(threading.Thread, StoppableThread): - def __init__(self): - threading.Thread.__init__(self, name="ReceiveQueueThread") + def __init__(self, num=0): + threading.Thread.__init__(self, name="ReceiveQueue_%i" %(num)) self.initStop() - self.name = "ReceiveQueueThread" - logger.info("init receive queue thread") + self.name = "ReceiveQueue_%i" % (num) + logger.info("init receive queue thread %i", num) def run(self): while not self._stopped and state.shutdown == 0: try: dest = receiveDataQueue.get(block=True, timeout=1) - receiveDataQueue.task_done() except Queue.Empty: continue @@ -44,3 +43,4 @@ class ReceiveQueueThread(threading.Thread, StoppableThread): # AttributeError = state isn't implemented except (KeyError, AttributeError): pass + receiveDataQueue.task_done() From 3941b39136206a50c90fb1cad4d320bb5d70c676 Mon Sep 17 00:00:00 2001 From: Peter Surda Date: Mon, 10 Jul 2017 07:10:05 +0200 Subject: [PATCH 156/407] Randomise node id - in order to detect if it's connected to to itself, PyBitmessage now uses a per-connection id rather than a global one --- src/network/connectionpool.py | 9 +++++++++ src/network/tcp.py | 12 +++++++++--- src/protocol.py | 7 +++++-- 3 files changed, 23 insertions(+), 5 deletions(-) diff --git a/src/network/connectionpool.py b/src/network/connectionpool.py index ba5481da..be7b8ceb 100644 --- a/src/network/connectionpool.py +++ b/src/network/connectionpool.py @@ -65,6 +65,15 @@ class BMConnectionPool(object): return self.udpSockets[addr] raise KeyError + def isAlreadyConnected(self, nodeid): + for i in self.inboundConnections.values() + self.outboundConnections.values(): + try: + if nodeid == i.nodeid: + return True + except AttributeError: + pass + return False + def addConnection(self, connection): if isinstance(connection, network.udp.UDPSocket): return diff --git a/src/network/tcp.py b/src/network/tcp.py index 75ddea1c..01b19817 100644 --- a/src/network/tcp.py +++ b/src/network/tcp.py @@ -11,6 +11,7 @@ import traceback from addresses import calculateInventoryHash from debug import logger +from helper_random import randomBytes from inventory import Inventory import knownnodes from network.advanceddispatcher import AdvancedDispatcher @@ -47,6 +48,7 @@ class TCPConnection(BMProto, TLSDispatcher): TLSDispatcher.__init__(self, sock, server_side=True) self.connectedAt = time.time() logger.debug("Received connection from %s:%i", self.destination.host, self.destination.port) + self.nodeid = randomBytes(8) elif address is not None and sock is not None: TLSDispatcher.__init__(self, sock, server_side=False) self.isOutbound = True @@ -187,7 +189,9 @@ class TCPConnection(BMProto, TLSDispatcher): if e.errno in asyncore._DISCONNECTED: logger.debug("%s:%i: Connection failed: %s" % (self.destination.host, self.destination.port, str(e))) return - self.append_write_buf(protocol.assembleVersionMessage(self.destination.host, self.destination.port, network.connectionpool.BMConnectionPool().streams, False)) + self.nodeid = randomBytes(8) + self.append_write_buf(protocol.assembleVersionMessage(self.destination.host, self.destination.port, \ + network.connectionpool.BMConnectionPool().streams, False, nodeid=self.nodeid)) #print "%s:%i: Sending version" % (self.destination.host, self.destination.port) self.connectedAt = time.time() receiveDataQueue.put(self.destination) @@ -220,8 +224,9 @@ class Socks5BMConnection(Socks5Connection, TCPConnection): def state_proxy_handshake_done(self): Socks5Connection.state_proxy_handshake_done(self) + self.nodeid = randomBytes(8) self.append_write_buf(protocol.assembleVersionMessage(self.destination.host, self.destination.port, \ - network.connectionpool.BMConnectionPool().streams, False)) + network.connectionpool.BMConnectionPool().streams, False, nodeid=self.nodeid)) self.set_state("bm_header", expectBytes=protocol.Header.size) return True @@ -234,8 +239,9 @@ class Socks4aBMConnection(Socks4aConnection, TCPConnection): def state_proxy_handshake_done(self): Socks4aConnection.state_proxy_handshake_done(self) + self.nodeid = randomBytes(8) self.append_write_buf(protocol.assembleVersionMessage(self.destination.host, self.destination.port, \ - network.connectionpool.BMConnectionPool().streams, False)) + network.connectionpool.BMConnectionPool().streams, False, nodeid=self.nodeid)) self.set_state("bm_header", expectBytes=protocol.Header.size) return True diff --git a/src/protocol.py b/src/protocol.py index d7bd5b8c..c2a58de8 100644 --- a/src/protocol.py +++ b/src/protocol.py @@ -186,7 +186,7 @@ def CreatePacket(command, payload=''): b[Header.size:] = payload return bytes(b) -def assembleVersionMessage(remoteHost, remotePort, participatingStreams, server = False): +def assembleVersionMessage(remoteHost, remotePort, participatingStreams, server = False, nodeid = None): payload = '' payload += pack('>L', 3) # protocol version. payload += pack('>q', NODE_NETWORK|(NODE_SSL if haveSSL(server) else 0)) # bitflags of the services I offer. @@ -217,7 +217,10 @@ def assembleVersionMessage(remoteHost, remotePort, participatingStreams, server payload += pack('>H', BMConfigParser().getint('bitmessagesettings', 'port')) random.seed() - payload += eightBytesOfRandomDataUsedToDetectConnectionsToSelf + if nodeid is not None: + payload += nodeid[0:8] + else: + payload += eightBytesOfRandomDataUsedToDetectConnectionsToSelf userAgent = '/PyBitmessage:' + softwareVersion + '/' payload += encodeVarint(len(userAgent)) payload += userAgent From 853c8561ec4b936531902a28e33f087686332907 Mon Sep 17 00:00:00 2001 From: Peter Surda Date: Mon, 10 Jul 2017 07:12:52 +0200 Subject: [PATCH 157/407] Per connection node id part 2 - forgot to include this in the previous commit --- src/network/bmproto.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/network/bmproto.py b/src/network/bmproto.py index 78149726..d2b5fa1f 100644 --- a/src/network/bmproto.py +++ b/src/network/bmproto.py @@ -362,7 +362,7 @@ class BMProto(AdvancedDispatcher, ObjectTracker): self.verackSent = True if not self.isOutbound: self.append_write_buf(protocol.assembleVersionMessage(self.destination.host, self.destination.port, \ - network.connectionpool.BMConnectionPool().streams, True)) + network.connectionpool.BMConnectionPool().streams, True, nodeid=self.nodeid)) #print "%s:%i: Sending version" % (self.destination.host, self.destination.port) if ((self.services & protocol.NODE_SSL == protocol.NODE_SSL) and protocol.haveSSL(not self.isOutbound)): @@ -414,7 +414,7 @@ class BMProto(AdvancedDispatcher, ObjectTracker): return False except: pass - if self.nonce == protocol.eightBytesOfRandomDataUsedToDetectConnectionsToSelf: + if network.connectionpool.BMConnectionPool().isAlreadyConnected(self.nonce): self.append_write_buf(protocol.assembleErrorMessage(fatal=2, errorText="I'm connected to myself. Closing connection.")) logger.debug ("Closed connection to %s because I'm connected to myself.", From db2d78c9b6da261ee58bb8a4385bd124c559423b Mon Sep 17 00:00:00 2001 From: Peter Surda Date: Mon, 10 Jul 2017 07:15:27 +0200 Subject: [PATCH 158/407] Make protocol decoder less recursive - apparently, recursion has bad performance in Python, so the decoder is now flat, except when parsing "version" command --- src/network/bmproto.py | 151 +++++++++++++++++++++++++---------------- 1 file changed, 92 insertions(+), 59 deletions(-) diff --git a/src/network/bmproto.py b/src/network/bmproto.py index d2b5fa1f..9aac3dda 100644 --- a/src/network/bmproto.py +++ b/src/network/bmproto.py @@ -152,8 +152,8 @@ class BMProto(AdvancedDispatcher, ObjectTracker): return Node(services, host, port) def decode_payload_content(self, pattern = "v"): - # l = varint indicating the length of the next array - # L = varint indicating the length of the next item + # L = varint indicating the length of the next array + # l = varint indicating the length of the next item # v = varint (or array) # H = uint16 # I = uint32 @@ -163,68 +163,101 @@ class BMProto(AdvancedDispatcher, ObjectTracker): # 0-9 = length of the next item # , = end of array - retval = [] + def decode_simple(self, char="v"): + if char == "v": + return self.decode_payload_varint() + if char == "i": + return self.decode_payload_node() + if char == "H": + self.payloadOffset += 2 + return struct.unpack(">H", self.payload[self.payloadOffset-2:self.payloadOffset])[0] + if char == "I": + self.payloadOffset += 4 + return struct.unpack(">I", self.payload[self.payloadOffset-4:self.payloadOffset])[0] + if char == "Q": + self.payloadOffset += 8 + return struct.unpack(">Q", self.payload[self.payloadOffset-8:self.payloadOffset])[0] + size = None - i = 0 - try: - sys._getframe(200) - logger.error("Stack depth warning, pattern: %s", pattern) - return - except ValueError: - pass + isArray = False - while i < len(pattern): - if pattern[i] in "0123456789" and (i == 0 or pattern[i-1] not in "lL"): - if size is None: - size = 0 - size = size * 10 + int(pattern[i]) - i += 1 - continue - elif pattern[i] == "l" and size is None: + # size + # iterator starting from size counting to 0 + # isArray? + # subpattern + # position of parser in subpattern + # retval (array) + parserStack = [[1, 1, False, pattern, 0, []]] + + #try: + # sys._getframe(200) + # logger.error("Stack depth warning, pattern: %s", pattern) + # return + #except ValueError: + # pass + + while True: + i = parserStack[-1][3][parserStack[-1][4]] + if i in "0123456789" and (size is None or parserStack[-1][3][parserStack[-1][4]-1] not in "lL"): + try: + size = size * 10 + int(i) + except TypeError: + size = int(i) + isArray = False + elif i in "Ll" and size is None: size = self.decode_payload_varint() - i += 1 - continue - elif pattern[i] == "L" and size is None: - size = self.decode_payload_varint() - i += 1 - continue - if size is not None: - if pattern[i] == "s": - retval.append(self.payload[self.payloadOffset:self.payloadOffset + size]) - self.payloadOffset += size - i += 1 + if i == "L": + isArray = True else: - if "," in pattern[i:]: - subpattern = pattern[i:pattern.index(",")] - else: - subpattern = pattern[i:] - - for j in range(size): - if pattern[i-1:i] == "L": - retval.extend(self.decode_payload_content(subpattern)) - else: - retval.append(self.decode_payload_content(subpattern)) - i += len(subpattern) + isArray = False + elif size is not None: + if isArray: + parserStack.append([size, size, isArray, parserStack[-1][3][parserStack[-1][4]:], 0, []]) + parserStack[-2][4] = len(parserStack[-2][3]) + else: + for j in range(parserStack[-1][4], len(parserStack[-1][3])): + if parserStack[-1][3][j] not in "lL0123456789": + break + parserStack.append([size, size, isArray, parserStack[-1][3][parserStack[-1][4]:j+1], 0, []]) + size = None + continue + elif i == "s": + #if parserStack[-2][2]: + # parserStack[-1][5].append(self.payload[self.payloadOffset:self.payloadOffset + parserStack[-1][0]]) + #else: + parserStack[-1][5] = self.payload[self.payloadOffset:self.payloadOffset + parserStack[-1][0]] + self.payloadOffset += parserStack[-1][0] + parserStack[-1][1] = 0 + parserStack[-1][2] = True + #del parserStack[-1] + size = None + elif i in "viHIQ": + parserStack[-1][5].append(decode_simple(self, parserStack[-1][3][parserStack[-1][4]])) size = None else: - if pattern[i] == "v": - retval.append(self.decode_payload_varint()) - if pattern[i] == "i": - retval.append(self.decode_payload_node()) - if pattern[i] == "H": - retval.append(struct.unpack(">H", self.payload[self.payloadOffset:self.payloadOffset+2])[0]) - self.payloadOffset += 2 - if pattern[i] == "I": - retval.append(struct.unpack(">I", self.payload[self.payloadOffset:self.payloadOffset+4])[0]) - self.payloadOffset += 4 - if pattern[i] == "Q": - retval.append(struct.unpack(">Q", self.payload[self.payloadOffset:self.payloadOffset+8])[0]) - self.payloadOffset += 8 - i += 1 + size = None + for depth in range(len(parserStack) - 1, -1, -1): + parserStack[depth][4] += 1 + if parserStack[depth][4] >= len(parserStack[depth][3]): + parserStack[depth][1] -= 1 + parserStack[depth][4] = 0 + if depth > 0: + if parserStack[depth][2]: + parserStack[depth - 1][5].append(parserStack[depth][5]) + else: + parserStack[depth - 1][5].extend(parserStack[depth][5]) + parserStack[depth][5] = [] + if parserStack[depth][1] <= 0: + if depth == 0: + # we're done, at depth 0 counter is at 0 and pattern is done parsing + return parserStack[depth][5] + del parserStack[-1] + continue + break + break if self.payloadOffset > self.payloadLength: logger.debug("Insufficient data %i/%i", self.payloadOffset, self.payloadLength) raise BMProtoInsufficientDataError() - return retval def bm_command_error(self): fatalStatus, banTime, inventoryVector, errorText = self.decode_payload_content("vvlsls") @@ -232,7 +265,7 @@ class BMProto(AdvancedDispatcher, ObjectTracker): return True def bm_command_getdata(self): - items = self.decode_payload_content("L32s") + items = self.decode_payload_content("l32s") # skip? if time.time() < self.skipUntil: return True @@ -246,7 +279,7 @@ class BMProto(AdvancedDispatcher, ObjectTracker): return True def bm_command_inv(self): - items = self.decode_payload_content("L32s") + items = self.decode_payload_content("l32s") if len(items) >= BMProto.maxObjectCount: logger.error("Too many items in inv message!") @@ -294,7 +327,7 @@ class BMProto(AdvancedDispatcher, ObjectTracker): return True def _decode_addr(self): - return self.decode_payload_content("lQIQ16sH") + return self.decode_payload_content("LQIQ16sH") def bm_command_addr(self): addresses = self._decode_addr() @@ -344,7 +377,7 @@ class BMProto(AdvancedDispatcher, ObjectTracker): def bm_command_version(self): self.remoteProtocolVersion, self.services, self.timestamp, self.sockNode, self.peerNode, self.nonce, \ - self.userAgent, self.streams = self.decode_payload_content("IQQiiQlslv") + self.userAgent, self.streams = self.decode_payload_content("IQQiiQlsLv") self.nonce = struct.pack('>Q', self.nonce) self.timeOffset = self.timestamp - int(time.time()) logger.debug("remoteProtocolVersion: %i", self.remoteProtocolVersion) From f6d5d93bf249346fc7dbc215ed83e85b19b256ca Mon Sep 17 00:00:00 2001 From: Peter Surda Date: Mon, 10 Jul 2017 20:52:11 +0200 Subject: [PATCH 159/407] Multiple receive queues fix - forgot to commit busy handler --- src/network/advanceddispatcher.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/network/advanceddispatcher.py b/src/network/advanceddispatcher.py index dbe65e39..1c33f376 100644 --- a/src/network/advanceddispatcher.py +++ b/src/network/advanceddispatcher.py @@ -50,6 +50,8 @@ class AdvancedDispatcher(asyncore.dispatcher): break except AttributeError: raise + except BusyError: + return False return False def set_state(self, state, length=0, expectBytes=0): From dcc181bf75f4cac0288d300ceb235810ada93803 Mon Sep 17 00:00:00 2001 From: Peter Surda Date: Mon, 10 Jul 2017 23:18:58 +0200 Subject: [PATCH 160/407] Asyncore processing thread synchronisation - threre was a synchronisation problem where one thread could process more data than another thread was expecting, leading to the thread crashing --- src/network/advanceddispatcher.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/network/advanceddispatcher.py b/src/network/advanceddispatcher.py index 1c33f376..005aa038 100644 --- a/src/network/advanceddispatcher.py +++ b/src/network/advanceddispatcher.py @@ -43,9 +43,12 @@ class AdvancedDispatcher(asyncore.dispatcher): def process(self): if not self.connected: return False - while len(self.read_buf) >= self.expectBytes: + loop = 0 + while True: try: with nonBlocking(self.processingLock): + if len(self.read_buf) < self.expectBytes: + return False if getattr(self, "state_" + str(self.state))() is False: break except AttributeError: From 4f19c37fdc3ace47784363066d103f5f99593fe7 Mon Sep 17 00:00:00 2001 From: Peter Surda Date: Tue, 11 Jul 2017 10:29:29 +0200 Subject: [PATCH 161/407] Parser fix for multi-level arrays --- src/network/bmproto.py | 1 + 1 file changed, 1 insertion(+) diff --git a/src/network/bmproto.py b/src/network/bmproto.py index 9aac3dda..30068d6d 100644 --- a/src/network/bmproto.py +++ b/src/network/bmproto.py @@ -219,6 +219,7 @@ class BMProto(AdvancedDispatcher, ObjectTracker): if parserStack[-1][3][j] not in "lL0123456789": break parserStack.append([size, size, isArray, parserStack[-1][3][parserStack[-1][4]:j+1], 0, []]) + parserStack[-2][4] += len(parserStack[-1][3]) - 1 size = None continue elif i == "s": From aa059d6f2fd45ec0d7de7bb2cd0477ff3552eb14 Mon Sep 17 00:00:00 2001 From: Peter Surda Date: Fri, 21 Jul 2017 07:47:18 +0200 Subject: [PATCH 162/407] Handle TLS errors in receivequeuethread - well at least EBADF, it seems to happen sometimes --- src/network/receivequeuethread.py | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/network/receivequeuethread.py b/src/network/receivequeuethread.py index a899851f..a617e16e 100644 --- a/src/network/receivequeuethread.py +++ b/src/network/receivequeuethread.py @@ -1,4 +1,6 @@ +import errno import Queue +import socket import sys import threading import time @@ -43,4 +45,9 @@ class ReceiveQueueThread(threading.Thread, StoppableThread): # AttributeError = state isn't implemented except (KeyError, AttributeError): pass + except socket.error as err: + if err.errno == errno.EBADF: + BMConnectionPool().getConnectionByAddr(dest).set_state("close", 0) + else: + logger.error("Socket error: %s", str(err)) receiveDataQueue.task_done() From 2530c62050e9bcf26381233fa0f4b95f6f90e62e Mon Sep 17 00:00:00 2001 From: Peter Surda Date: Fri, 21 Jul 2017 07:49:34 +0200 Subject: [PATCH 163/407] epoll throws IOError rather than select.error --- src/network/asyncore_pollchoose.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/network/asyncore_pollchoose.py b/src/network/asyncore_pollchoose.py index 07b2c120..c1296744 100644 --- a/src/network/asyncore_pollchoose.py +++ b/src/network/asyncore_pollchoose.py @@ -310,6 +310,10 @@ def epoll_poller(timeout=0.0, map=None): pass try: r = epoll_poller.pollster.poll(timeout) + except IOError as e: + if e.errno != EINTR: + raise + r = [] except select.error, err: if err.args[0] != EINTR: raise From a29f7534ee54a6b93498ec13e581ee2da22dfc94 Mon Sep 17 00:00:00 2001 From: Peter Surda Date: Fri, 21 Jul 2017 09:06:02 +0200 Subject: [PATCH 164/407] Add EINTR handler for select and poll pollers --- src/network/asyncore_pollchoose.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/network/asyncore_pollchoose.py b/src/network/asyncore_pollchoose.py index c1296744..3b21d3da 100644 --- a/src/network/asyncore_pollchoose.py +++ b/src/network/asyncore_pollchoose.py @@ -203,7 +203,7 @@ def select_poller(timeout=0.0, map=None): except KeyboardInterrupt: return except socket.error as err: - if err.args[0] in (EBADF, WSAENOTSOCK): + if err.args[0] in (EBADF, WSAENOTSOCK, EINTR): return for fd in random.sample(r, len(r)): @@ -263,6 +263,9 @@ def poll_poller(timeout=0.0, map=None): r = poll_poller.pollster.poll(timeout) except KeyboardInterrupt: r = [] + except socket.error as err: + if err.args[0] in (EBADF, WSAENOTSOCK, EINTR): + return for fd, flags in random.sample(r, len(r)): obj = map.get(fd) if obj is None: From 20cbac9752b103465064081f72fc8e038e9dfcdb Mon Sep 17 00:00:00 2001 From: Peter Surda Date: Fri, 28 Jul 2017 08:54:34 +0200 Subject: [PATCH 165/407] Fix daemonize for Windows Fixes #1034 --- src/bitmessagemain.py | 22 ++++++++++++++++------ 1 file changed, 16 insertions(+), 6 deletions(-) diff --git a/src/bitmessagemain.py b/src/bitmessagemain.py index 3e0a1c84..50c138a2 100755 --- a/src/bitmessagemain.py +++ b/src/bitmessagemain.py @@ -316,14 +316,24 @@ class Main: BMConfigParser().remove_option('bitmessagesettings', 'dontconnect') def daemonize(self): - if os.fork(): - exit(0) - shared.thisapp.lock() # relock + try: + if os.fork(): + exit(0) + except AttributeError: + # fork not implemented + pass + else: + shared.thisapp.lock() # relock os.umask(0) os.setsid() - if os.fork(): - exit(0) - shared.thisapp.lock() # relock + try: + if os.fork(): + exit(0) + except AttributeError: + # fork not implemented + pass + else: + shared.thisapp.lock() # relock shared.thisapp.lockPid = None # indicate we're the final child sys.stdout.flush() sys.stderr.flush() From 501f07dd34676fe472620b2a8274a01f389f158a Mon Sep 17 00:00:00 2001 From: Peter Surda Date: Fri, 28 Jul 2017 09:19:53 +0200 Subject: [PATCH 166/407] Setsid is not available on Windows - wrap an error handler around it --- src/bitmessagemain.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/bitmessagemain.py b/src/bitmessagemain.py index 50c138a2..4a7227e9 100755 --- a/src/bitmessagemain.py +++ b/src/bitmessagemain.py @@ -325,7 +325,11 @@ class Main: else: shared.thisapp.lock() # relock os.umask(0) - os.setsid() + try: + os.setsid() + except AttributeError: + # setsid not implemented + pass try: if os.fork(): exit(0) From e7382b7714947c69ad0427890bf4618ba3716ff9 Mon Sep 17 00:00:00 2001 From: Peter Surda Date: Fri, 28 Jul 2017 09:39:49 +0200 Subject: [PATCH 167/407] Write PID into the lock file --- src/singleinstance.py | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/src/singleinstance.py b/src/singleinstance.py index a6ac4552..d6337ff6 100644 --- a/src/singleinstance.py +++ b/src/singleinstance.py @@ -44,7 +44,7 @@ class singleinstance: # file already exists, we try to remove (in case previous execution was interrupted) if os.path.exists(self.lockfile): os.unlink(self.lockfile) - self.fd = os.open(self.lockfile, os.O_CREAT | os.O_EXCL | os.O_RDWR) + self.fd = os.open(self.lockfile, os.O_CREAT | os.O_EXCL | os.O_RDWR | os.O_TRUNC) except OSError: type, e, tb = sys.exc_info() if e.errno == 13: @@ -52,6 +52,9 @@ class singleinstance: sys.exit(-1) print(e.errno) raise + else: + pidLine = "%i\n" % self.lockPid + self.fd.write(pidLine) else: # non Windows self.fp = open(self.lockfile, 'w') try: @@ -63,6 +66,10 @@ class singleinstance: except IOError: print 'Another instance of this application is already running' sys.exit(-1) + else: + pidLine = "%i\n" % self.lockPid + self.fp.write(pidLine) + self.fp.flush() def cleanup(self): if not self.initialized: From 3e6de7a9ad10a14d1289f913b6ef0a4358578438 Mon Sep 17 00:00:00 2001 From: Peter Surda Date: Fri, 28 Jul 2017 19:21:56 +0200 Subject: [PATCH 168/407] Flush PID file on unix as well --- src/singleinstance.py | 1 + 1 file changed, 1 insertion(+) diff --git a/src/singleinstance.py b/src/singleinstance.py index d6337ff6..8a7fb68e 100644 --- a/src/singleinstance.py +++ b/src/singleinstance.py @@ -55,6 +55,7 @@ class singleinstance: else: pidLine = "%i\n" % self.lockPid self.fd.write(pidLine) + self.fd.flush() else: # non Windows self.fp = open(self.lockfile, 'w') try: From 7a4551e1e72ce5ffd6fc359437435c077103e027 Mon Sep 17 00:00:00 2001 From: Peter Surda Date: Sun, 30 Jul 2017 09:36:20 +0200 Subject: [PATCH 169/407] Fix signal handler in daemon mode - signal handler requires the main thread to run --- src/bitmessagemain.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/bitmessagemain.py b/src/bitmessagemain.py index 4a7227e9..388a6068 100755 --- a/src/bitmessagemain.py +++ b/src/bitmessagemain.py @@ -27,6 +27,7 @@ import socket import ctypes from struct import pack from subprocess import call +from time import sleep from api import MySimpleXMLRPCRequestHandler, StoppableXMLRPCServer from helper_startup import isOurOperatingSystemLimitedToHavingVeryFewHalfOpenConnections @@ -315,6 +316,10 @@ class Main: else: BMConfigParser().remove_option('bitmessagesettings', 'dontconnect') + if daemon: + while state.shutdown == 0: + sleep(1) + def daemonize(self): try: if os.fork(): From 8f14fb05a1356ebe80de82ba08d248261bb700a6 Mon Sep 17 00:00:00 2001 From: Peter Surda Date: Sat, 5 Aug 2017 10:14:15 +0200 Subject: [PATCH 170/407] UDP socket setsockopt fix --- src/network/udp.py | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/src/network/udp.py b/src/network/udp.py index 5c19fa69..bf52ebd5 100644 --- a/src/network/udp.py +++ b/src/network/udp.py @@ -33,6 +33,7 @@ class UDPSocket(BMProto): self.create_socket(socket.AF_INET6, socket.SOCK_DGRAM) else: self.create_socket(socket.AF_INET, socket.SOCK_DGRAM) + self.set_socket_reuse() logger.info("Binding UDP socket to %s:%i", host, UDPSocket.port) self.socket.bind((host, UDPSocket.port)) #BINDTODEVICE is only available on linux and requires root @@ -42,12 +43,7 @@ class UDPSocket(BMProto): #except AttributeError: else: self.socket = sock - self.socket.setsockopt(socket.SOL_SOCKET, socket.SO_BROADCAST, 1) - self.socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) - try: - self.socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEPORT, 1) - except AttributeError: - pass + self.set_socket_reuse() self.listening = state.Peer(self.socket.getsockname()[0], self.socket.getsockname()[1]) self.destination = state.Peer(self.socket.getsockname()[0], self.socket.getsockname()[1]) ObjectTracker.__init__(self) @@ -55,6 +51,14 @@ class UDPSocket(BMProto): self.connected = True self.set_state("bm_header", expectBytes=protocol.Header.size) + def set_socket_reuse(self): + self.socket.setsockopt(socket.SOL_SOCKET, socket.SO_BROADCAST, 1) + self.socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) + try: + self.socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEPORT, 1) + except AttributeError: + pass + def state_bm_command(self): BMProto.state_bm_command(self) From 5108d08ac904231536d9d972c49cd4686f84ca65 Mon Sep 17 00:00:00 2001 From: Peter Surda Date: Sun, 6 Aug 2017 18:18:21 +0200 Subject: [PATCH 171/407] Windows asyncore error handler fix - WSAEWOULDBLOCK is now checked on connect and accept --- src/network/asyncore_pollchoose.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/network/asyncore_pollchoose.py b/src/network/asyncore_pollchoose.py index 3b21d3da..ba156990 100644 --- a/src/network/asyncore_pollchoose.py +++ b/src/network/asyncore_pollchoose.py @@ -550,7 +550,7 @@ class dispatcher: self.connected = False self.connecting = True err = self.socket.connect_ex(address) - if err in (EINPROGRESS, EALREADY, EWOULDBLOCK) \ + if err in (EINPROGRESS, EALREADY, EWOULDBLOCK, WSAEWOULDBLOCK) \ or err == EINVAL and os.name in ('nt', 'ce'): self.addr = address return @@ -567,7 +567,7 @@ class dispatcher: except TypeError: return None except socket.error as why: - if why.args[0] in (EWOULDBLOCK, ECONNABORTED, EAGAIN, ENOTCONN): + if why.args[0] in (EWOULDBLOCK, WSAEWOULDBLOCK, ECONNABORTED, EAGAIN, ENOTCONN): return None else: raise From 578c5dd49542b565578bec3b7c8836d624ffa81b Mon Sep 17 00:00:00 2001 From: Peter Surda Date: Sun, 6 Aug 2017 18:29:08 +0200 Subject: [PATCH 172/407] Fix windows PID file --- src/singleinstance.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/singleinstance.py b/src/singleinstance.py index 8a7fb68e..78a0cb33 100644 --- a/src/singleinstance.py +++ b/src/singleinstance.py @@ -54,8 +54,7 @@ class singleinstance: raise else: pidLine = "%i\n" % self.lockPid - self.fd.write(pidLine) - self.fd.flush() + os.write(self.fd, pidLine) else: # non Windows self.fp = open(self.lockfile, 'w') try: From 5895dc2f1ff21ccc69f7571582f90d52875d51a5 Mon Sep 17 00:00:00 2001 From: Peter Surda Date: Sun, 6 Aug 2017 20:39:14 +0200 Subject: [PATCH 173/407] Asyncore Windows error handling - windows behaves somewhat differently when using select --- src/network/asyncore_pollchoose.py | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/src/network/asyncore_pollchoose.py b/src/network/asyncore_pollchoose.py index ba156990..db6c1f4a 100644 --- a/src/network/asyncore_pollchoose.py +++ b/src/network/asyncore_pollchoose.py @@ -67,9 +67,14 @@ try: from errno import WSAENOTSOCK except (ImportError, AttributeError): WSAENOTSOCK = ENOTSOCK +try: + from errno import WSAECONNRESET +except (ImportError, AttributeError): + WSEACONNRESET = ECONNRESET _DISCONNECTED = frozenset((ECONNRESET, ENOTCONN, ESHUTDOWN, ECONNABORTED, EPIPE, - EBADF, ECONNREFUSED, EHOSTUNREACH, ENETUNREACH, ETIMEDOUT)) + EBADF, ECONNREFUSED, EHOSTUNREACH, ENETUNREACH, ETIMEDOUT, + WSAECONNRESET)) OP_READ = 1 OP_WRITE = 2 @@ -203,7 +208,10 @@ def select_poller(timeout=0.0, map=None): except KeyboardInterrupt: return except socket.error as err: - if err.args[0] in (EBADF, WSAENOTSOCK, EINTR): + if err.args[0] in (EBADF EINTR): + return + except Exception as err: + if err.args[0] in (WSAENOTSOCK, ): return for fd in random.sample(r, len(r)): @@ -686,6 +694,9 @@ class dispatcher: # like we would in a subclassed handle_read() that received no # data self.handle_close() + elif sys.platform.startswith("win"): + # async connect failed + self.handle_close() else: self.handle_expt() From 38872159fb9dee140288504cde1a31b5bbdea85c Mon Sep 17 00:00:00 2001 From: Peter Surda Date: Sun, 6 Aug 2017 20:40:35 +0200 Subject: [PATCH 174/407] Typo in previous commit --- src/network/asyncore_pollchoose.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/network/asyncore_pollchoose.py b/src/network/asyncore_pollchoose.py index db6c1f4a..91b41e23 100644 --- a/src/network/asyncore_pollchoose.py +++ b/src/network/asyncore_pollchoose.py @@ -208,7 +208,7 @@ def select_poller(timeout=0.0, map=None): except KeyboardInterrupt: return except socket.error as err: - if err.args[0] in (EBADF EINTR): + if err.args[0] in (EBADF, EINTR): return except Exception as err: if err.args[0] in (WSAENOTSOCK, ): From 4564d37f5b969de760eec1ac7a12e0e9501ed121 Mon Sep 17 00:00:00 2001 From: Peter Surda Date: Sun, 6 Aug 2017 21:26:25 +0200 Subject: [PATCH 175/407] Typo in previous commits --- src/network/asyncore_pollchoose.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/network/asyncore_pollchoose.py b/src/network/asyncore_pollchoose.py index 91b41e23..2b874c0f 100644 --- a/src/network/asyncore_pollchoose.py +++ b/src/network/asyncore_pollchoose.py @@ -70,7 +70,7 @@ except (ImportError, AttributeError): try: from errno import WSAECONNRESET except (ImportError, AttributeError): - WSEACONNRESET = ECONNRESET + WSAECONNRESET = ECONNRESET _DISCONNECTED = frozenset((ECONNRESET, ENOTCONN, ESHUTDOWN, ECONNABORTED, EPIPE, EBADF, ECONNREFUSED, EHOSTUNREACH, ENETUNREACH, ETIMEDOUT, From f338c00f8efc8868743882875ba9b9b5d92d90a0 Mon Sep 17 00:00:00 2001 From: Peter Surda Date: Sun, 6 Aug 2017 21:29:54 +0200 Subject: [PATCH 176/407] Change peer discovery tracking from queue to a dict - with a queue, a situation could occur when new entries are appended but nothing is polling the queue --- src/class_singleCleaner.py | 9 ++++++ src/network/connectionchooser.py | 49 ++++++++++++++++++-------------- src/network/udp.py | 4 +-- src/queues.py | 1 - src/state.py | 2 ++ 5 files changed, 40 insertions(+), 25 deletions(-) diff --git a/src/class_singleCleaner.py b/src/class_singleCleaner.py index c21d8cb1..5cc35b41 100644 --- a/src/class_singleCleaner.py +++ b/src/class_singleCleaner.py @@ -38,6 +38,7 @@ resends msg messages in 5 days (then 10 days, then 20 days, etc...) class singleCleaner(threading.Thread, StoppableThread): cycleLength = 300 + expireDiscoveredPeers = 300 def __init__(self): threading.Thread.__init__(self, name="singleCleaner") @@ -126,6 +127,14 @@ class singleCleaner(threading.Thread, StoppableThread): for connection in BMConnectionPool().inboundConnections.values() + BMConnectionPool().outboundConnections.values(): connection.clean() + # discovery tracking + exp = time.time() - singleCleander.expireDiscoveredPeers + reaper = (k for k, v in state.discoveredPeers.items() if v < exp) + for k in reaper: + try: + del state.discoveredPeers[k] + except KeyError: + pass # TODO: cleanup pending upload / download if state.shutdown == 0: diff --git a/src/network/connectionchooser.py b/src/network/connectionchooser.py index 2cce5036..b4d7a37f 100644 --- a/src/network/connectionchooser.py +++ b/src/network/connectionchooser.py @@ -3,9 +3,15 @@ import random from bmconfigparser import BMConfigParser import knownnodes -from queues import portCheckerQueue, peerDiscoveryQueue +from queues import portCheckerQueue import state +def getDiscoveredPeer(stream): + try: + peer = random.choice(state.discoveredPeers.keys()) + except (IndexError, KeyError): + raise ValueError + def chooseConnection(stream): haveOnion = BMConfigParser().safeGet("bitmessagesettings", "socksproxytype")[0:5] == 'SOCKS' if state.trustedPeer: @@ -13,26 +19,25 @@ def chooseConnection(stream): try: retval = portCheckerQueue.get(False) portCheckerQueue.task_done() + return retval except Queue.Empty: + pass + if random.choice((False, True)): + return getDiscoveredPeer(stream) + for i in range(50): + peer = random.choice(knownnodes.knownNodes[stream].keys()) try: - retval = peerDiscoveryQueue.get(False) - peerDiscoveryQueue.task_done() - except Queue.Empty: - for i in range(50): - peer = random.choice(knownnodes.knownNodes[stream].keys()) - try: - rating = knownnodes.knownNodes[stream][peer]["rating"] - except TypeError: - print "Error in %s" % (peer) - rating = 0 - if haveOnion and peer.host.endswith('.onion') and rating > 0: - rating *= 10 - if rating > 1: - rating = 1 - try: - if 0.05/(1.0-rating) > random.random(): - return peer - except ZeroDivisionError: - return peer - raise ValueError - return retval + rating = knownnodes.knownNodes[stream][peer]["rating"] + except TypeError: + print "Error in %s" % (peer) + rating = 0 + if haveOnion and peer.host.endswith('.onion') and rating > 0: + rating *= 10 + if rating > 1: + rating = 1 + try: + if 0.05/(1.0-rating) > random.random(): + return peer + except ZeroDivisionError: + return peer + raise ValueError diff --git a/src/network/udp.py b/src/network/udp.py index bf52ebd5..910e2430 100644 --- a/src/network/udp.py +++ b/src/network/udp.py @@ -9,7 +9,7 @@ from network.bmobject import BMObject, BMObjectInsufficientPOWError, BMObjectInv import network.asyncore_pollchoose as asyncore from network.objectracker import ObjectTracker -from queues import objectProcessorQueue, peerDiscoveryQueue, UISignalQueue, receiveDataQueue +from queues import objectProcessorQueue, UISignalQueue, receiveDataQueue import state import protocol @@ -100,7 +100,7 @@ class UDPSocket(BMProto): return True logger.debug("received peer discovery from %s:%i (port %i):", self.destination.host, self.destination.port, remoteport) if self.local: - peerDiscoveryQueue.put(state.Peer(self.destination.host, remoteport)) + state.discoveredPeers[state.Peer(self.destination.host, remoteport)] = time.time return True def bm_command_portcheck(self): diff --git a/src/queues.py b/src/queues.py index f768c59f..e8923dbd 100644 --- a/src/queues.py +++ b/src/queues.py @@ -11,7 +11,6 @@ objectProcessorQueue = ObjectProcessorQueue() invQueue = MultiQueue() addrQueue = MultiQueue() portCheckerQueue = Queue.Queue() -peerDiscoveryQueue = Queue.Queue() receiveDataQueue = Queue.Queue() apiAddressGeneratorReturnQueue = Queue.Queue( ) # The address generator thread uses this queue to get information back to the API thread. diff --git a/src/state.py b/src/state.py index 08da36eb..c9cb3d1c 100644 --- a/src/state.py +++ b/src/state.py @@ -41,6 +41,8 @@ ownAddresses = {} # security. trustedPeer = None +discoveredPeers = {} + Peer = collections.namedtuple('Peer', ['host', 'port']) def resetNetworkProtocolAvailability(): From d9e4f2ceb86437f5cb6e851aa5e4b2297adf3232 Mon Sep 17 00:00:00 2001 From: Peter Surda Date: Sun, 6 Aug 2017 21:38:23 +0200 Subject: [PATCH 177/407] Typo in previous commit --- src/class_singleCleaner.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/class_singleCleaner.py b/src/class_singleCleaner.py index 5cc35b41..c37c3ecf 100644 --- a/src/class_singleCleaner.py +++ b/src/class_singleCleaner.py @@ -128,7 +128,7 @@ class singleCleaner(threading.Thread, StoppableThread): connection.clean() # discovery tracking - exp = time.time() - singleCleander.expireDiscoveredPeers + exp = time.time() - singleCleaner.expireDiscoveredPeers reaper = (k for k, v in state.discoveredPeers.items() if v < exp) for k in reaper: try: From 0324958e9234ccbe66af12e15cbc7f5d7cb2c348 Mon Sep 17 00:00:00 2001 From: Peter Surda Date: Sun, 6 Aug 2017 23:05:54 +0200 Subject: [PATCH 178/407] Peer discovery fixes - incoming packets weren't correctly processed --- src/network/connectionchooser.py | 2 +- src/network/connectionpool.py | 4 ++-- src/network/udp.py | 4 ++-- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/network/connectionchooser.py b/src/network/connectionchooser.py index b4d7a37f..616a80f6 100644 --- a/src/network/connectionchooser.py +++ b/src/network/connectionchooser.py @@ -8,7 +8,7 @@ import state def getDiscoveredPeer(stream): try: - peer = random.choice(state.discoveredPeers.keys()) + return random.choice(state.discoveredPeers.keys()) except (IndexError, KeyError): raise ValueError diff --git a/src/network/connectionpool.py b/src/network/connectionpool.py index be7b8ceb..201432f0 100644 --- a/src/network/connectionpool.py +++ b/src/network/connectionpool.py @@ -61,8 +61,8 @@ class BMConnectionPool(object): return self.inboundConnections[addr.host] if addr in self.outboundConnections: return self.outboundConnections[addr] - if addr in self.udpSockets: - return self.udpSockets[addr] + if addr.host in self.udpSockets: + return self.udpSockets[addr.host] raise KeyError def isAlreadyConnected(self, nodeid): diff --git a/src/network/udp.py b/src/network/udp.py index 910e2430..8448da16 100644 --- a/src/network/udp.py +++ b/src/network/udp.py @@ -100,7 +100,7 @@ class UDPSocket(BMProto): return True logger.debug("received peer discovery from %s:%i (port %i):", self.destination.host, self.destination.port, remoteport) if self.local: - state.discoveredPeers[state.Peer(self.destination.host, remoteport)] = time.time + state.discoveredPeers[state.Peer(self.destination.host, remoteport)] = time.time() return True def bm_command_portcheck(self): @@ -143,7 +143,7 @@ class UDPSocket(BMProto): # overwrite the old buffer to avoid mixing data and so that self.local works correctly self.read_buf = recdata self.bm_proto_reset() - receiveDataQueue.put(self.destination) + receiveDataQueue.put(self.listening) def handle_write(self): try: From cc955cd69d7c0a9f41b98d88c85eaf91b2a8e6ba Mon Sep 17 00:00:00 2001 From: Peter Surda Date: Wed, 9 Aug 2017 17:29:23 +0200 Subject: [PATCH 179/407] Try new ports of binding fails - API and BM protocol will try random ports for binding if those configured are occupied --- src/api.py | 2 ++ src/bitmessagemain.py | 22 ++++++++++++++++++++-- src/network/asyncore_pollchoose.py | 5 +++++ src/network/connectionpool.py | 4 +++- src/network/tcp.py | 21 ++++++++++++++++++++- src/upnp.py | 13 ++++++++++++- 6 files changed, 62 insertions(+), 5 deletions(-) diff --git a/src/api.py b/src/api.py index 52fb8e6e..7c67f89f 100644 --- a/src/api.py +++ b/src/api.py @@ -55,6 +55,8 @@ class APIError(Exception): class StoppableXMLRPCServer(SimpleXMLRPCServer): + allow_reuse_address = True + def serve_forever(self): while state.shutdown == 0: self.handle_request() diff --git a/src/bitmessagemain.py b/src/bitmessagemain.py index 388a6068..17e97ffd 100755 --- a/src/bitmessagemain.py +++ b/src/bitmessagemain.py @@ -28,6 +28,7 @@ import ctypes from struct import pack from subprocess import call from time import sleep +from random import randint from api import MySimpleXMLRPCRequestHandler, StoppableXMLRPCServer from helper_startup import isOurOperatingSystemLimitedToHavingVeryFewHalfOpenConnections @@ -171,8 +172,25 @@ class singleAPI(threading.Thread, helper_threading.StoppableThread): pass def run(self): - se = StoppableXMLRPCServer((BMConfigParser().get('bitmessagesettings', 'apiinterface'), BMConfigParser().getint( - 'bitmessagesettings', 'apiport')), MySimpleXMLRPCRequestHandler, True, True) + port = BMConfigParser().getint('bitmessagesettings', 'apiport') + try: + from errno import WSAEADDRINUSE + except (ImportError, AttributeError): + errno.WSAEADDRINUSE = errno.EADDRINUSE + for attempt in range(50): + try: + if attempt > 0: + port = randint(32767, 65535) + se = StoppableXMLRPCServer((BMConfigParser().get('bitmessagesettings', 'apiinterface'), port), + MySimpleXMLRPCRequestHandler, True, True) + except socket.error as e: + if e.errno in (errno.EADDRINUSE, errno.WSAEADDRINUSE): + continue + else: + if attempt > 0: + BMConfigParser().set("bitmessagesettings", "apiport", str(port)) + BMConfigParser().save() + break se.register_introspection_functions() se.serve_forever() diff --git a/src/network/asyncore_pollchoose.py b/src/network/asyncore_pollchoose.py index 2b874c0f..c2568e9f 100644 --- a/src/network/asyncore_pollchoose.py +++ b/src/network/asyncore_pollchoose.py @@ -58,6 +58,7 @@ import os from errno import EALREADY, EINPROGRESS, EWOULDBLOCK, ECONNRESET, EINVAL, \ ENOTCONN, ESHUTDOWN, EISCONN, EBADF, ECONNABORTED, EPIPE, EAGAIN, \ ECONNREFUSED, EHOSTUNREACH, ENETUNREACH, ENOTSOCK, EINTR, ETIMEDOUT, \ + EADDRINUSE, \ errorcode try: from errno import WSAEWOULDBLOCK @@ -71,6 +72,10 @@ try: from errno import WSAECONNRESET except (ImportError, AttributeError): WSAECONNRESET = ECONNRESET +try: + from errno import WSAEADDRINUSE +except (ImportError, AttributeError): + WSAEADDRINUSE = EADDRINUSE _DISCONNECTED = frozenset((ECONNRESET, ENOTCONN, ESHUTDOWN, ECONNABORTED, EPIPE, EBADF, ECONNREFUSED, EHOSTUNREACH, ENETUNREACH, ETIMEDOUT, diff --git a/src/network/connectionpool.py b/src/network/connectionpool.py index 201432f0..8474e84c 100644 --- a/src/network/connectionpool.py +++ b/src/network/connectionpool.py @@ -119,7 +119,9 @@ class BMConnectionPool(object): def startListening(self): host = self.getListeningIP() port = BMConfigParser().safeGetInt("bitmessagesettings", "port") - self.listeningSockets[state.Peer(host, port)] = network.tcp.TCPServer(host=host, port=port) + # correct port even if it changed + ls = network.tcp.TCPServer(host=host, port=port) + self.listeningSockets[ls.destination] = ls def startUDPSocket(self, bind=None): if bind is None: diff --git a/src/network/tcp.py b/src/network/tcp.py index 01b19817..0fcbc160 100644 --- a/src/network/tcp.py +++ b/src/network/tcp.py @@ -252,10 +252,29 @@ class TCPServer(AdvancedDispatcher): AdvancedDispatcher.__init__(self) self.create_socket(socket.AF_INET, socket.SOCK_STREAM) self.set_reuse_addr() - self.bind((host, port)) + for attempt in range(50): + try: + if attempt > 0: + port = random.randint(32767, 65535) + self.bind((host, port)) + except socket.error as e: + if e.errno in (asyncore.EADDRINUSE, asyncore.WSAEADDRINUSE): + continue + else: + if attempt > 0: + BMConfigParser().set("bitmessagesettings", "port", str(port)) + BMConfigParser().save() + break self.destination = state.Peer(host, port) + self.bound = True self.listen(5) + def is_bound(self): + try: + return self.bound + except AttributeError: + return False + def handle_accept(self): pair = self.accept() if pair is not None: diff --git a/src/upnp.py b/src/upnp.py index 6c245345..ff657619 100644 --- a/src/upnp.py +++ b/src/upnp.py @@ -7,6 +7,7 @@ from struct import unpack, pack import threading import time from bmconfigparser import BMConfigParser +from network.connectionpool import BMConnectionPool from helper_threading import * import queues import shared @@ -179,7 +180,6 @@ class uPnPThread(threading.Thread, StoppableThread): def __init__ (self): threading.Thread.__init__(self, name="uPnPThread") - self.localPort = BMConfigParser().getint('bitmessagesettings', 'port') try: self.extPort = BMConfigParser().getint('bitmessagesettings', 'extport') except: @@ -199,6 +199,17 @@ class uPnPThread(threading.Thread, StoppableThread): logger.debug("Starting UPnP thread") logger.debug("Local IP: %s", self.localIP) lastSent = 0 + + # wait until asyncore binds so that we know the listening port + bound = False + while state.shutdown == 0 and not self._stopped and not bound: + for s in BMConnectionPool().listeningSockets.values(): + if s.is_bound(): + bound = True + if not bound: + time.sleep(1) + + self.localPort = BMConfigParser().getint('bitmessagesettings', 'port') while state.shutdown == 0 and BMConfigParser().safeGetBoolean('bitmessagesettings', 'upnp'): if time.time() - lastSent > self.sendSleep and len(self.routers) == 0: try: From e071efac1a86e510c3e83d0b388485f1e3d7192b Mon Sep 17 00:00:00 2001 From: Peter Surda Date: Wed, 9 Aug 2017 17:29:48 +0200 Subject: [PATCH 180/407] Typo --- src/network/connectionpool.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/network/connectionpool.py b/src/network/connectionpool.py index 8474e84c..96c6a6b6 100644 --- a/src/network/connectionpool.py +++ b/src/network/connectionpool.py @@ -106,7 +106,7 @@ class BMConnectionPool(object): def getListeningIP(self): if BMConfigParser().safeGet("bitmessagesettings", "onionhostname").endswith(".onion"): - host = BMConfigParser().safeGet("bitmessagesettigns", "onionbindip") + host = BMConfigParser().safeGet("bitmessagesettings", "onionbindip") else: host = '127.0.0.1' if BMConfigParser().safeGetBoolean("bitmessagesettings", "sockslisten") or \ From 0b07b1c89a31ae3fcf6b26512498a30b89a5be62 Mon Sep 17 00:00:00 2001 From: Peter Surda Date: Wed, 9 Aug 2017 17:34:47 +0200 Subject: [PATCH 181/407] Peer discovery updates - allow loopback addresses, now you can bind different loopback IP addresses on a single system and they will auto-cross-connect - always listen for discovery on 0.0.0.0 - [network] - bind now also applies for the TCP socket as well as UDP socket - closing socket iterator fix --- src/bmconfigparser.py | 2 +- src/network/announcethread.py | 2 ++ src/network/connectionchooser.py | 7 ++++++- src/network/connectionpool.py | 16 ++++++++++------ src/network/udp.py | 3 ++- src/protocol.py | 4 +++- 6 files changed, 24 insertions(+), 10 deletions(-) diff --git a/src/bmconfigparser.py b/src/bmconfigparser.py index 094cd73d..b8bf790f 100644 --- a/src/bmconfigparser.py +++ b/src/bmconfigparser.py @@ -20,7 +20,7 @@ BMConfigDefaults = { }, "network": { "asyncore": True, - "bind": None, + "bind": '', }, "inventory": { "storage": "sqlite", diff --git a/src/network/announcethread.py b/src/network/announcethread.py index 26d9f9cf..a94eeb36 100644 --- a/src/network/announcethread.py +++ b/src/network/announcethread.py @@ -28,6 +28,8 @@ class AnnounceThread(threading.Thread, StoppableThread): def announceSelf(self): for connection in BMConnectionPool().udpSockets.values(): + if not connection.announcing: + continue for stream in state.streamsInWhichIAmParticipating: addr = (stream, state.Peer('127.0.0.1', BMConfigParser().safeGetInt("bitmessagesettings", "port")), time.time()) connection.append_write_buf(BMProto.assembleAddr([addr])) diff --git a/src/network/connectionchooser.py b/src/network/connectionchooser.py index 616a80f6..681291ad 100644 --- a/src/network/connectionchooser.py +++ b/src/network/connectionchooser.py @@ -8,9 +8,14 @@ import state def getDiscoveredPeer(stream): try: - return random.choice(state.discoveredPeers.keys()) + peer = random.choice(state.discoveredPeers.keys()) except (IndexError, KeyError): raise ValueError + try: + del state.discoveredPeers[peer] + except KeyError: + pass + return peer def chooseConnection(stream): haveOnion = BMConfigParser().safeGet("bitmessagesettings", "socksproxytype")[0:5] == 'SOCKS' diff --git a/src/network/connectionpool.py b/src/network/connectionpool.py index 96c6a6b6..3adc1772 100644 --- a/src/network/connectionpool.py +++ b/src/network/connectionpool.py @@ -113,7 +113,7 @@ class BMConnectionPool(object): BMConfigParser().get("bitmessagesettings", "socksproxytype") == "none": # python doesn't like bind + INADDR_ANY? #host = socket.INADDR_ANY - host = '' + host = BMConfigParser().get("network", "bind") return host def startListening(self): @@ -126,9 +126,12 @@ class BMConnectionPool(object): def startUDPSocket(self, bind=None): if bind is None: host = self.getListeningIP() - udpSocket = network.udp.UDPSocket(host=host) + udpSocket = network.udp.UDPSocket(host=host, announcing=True) else: - udpSocket = network.udp.UDPSocket(host=bind) + if bind is False: + udpSocket = network.udp.UDPSocket(announcing=False) + else: + udpSocket = network.udp.UDPSocket(host=bind, announcing=True) self.udpSockets[udpSocket.listening.host] = udpSocket def loop(self): @@ -192,19 +195,20 @@ class BMConnectionPool(object): self.startListening() logger.info('Listening for incoming connections.') if not self.udpSockets: - if BMConfigParser().safeGet("network", "bind") is None: + if BMConfigParser().safeGet("network", "bind") == '': self.startUDPSocket() else: for bind in re.sub("[^\w.]+", " ", BMConfigParser().safeGet("network", "bind")).split(): self.startUDPSocket(bind) + self.startUDPSocket(False) logger.info('Starting UDP socket(s).') else: if self.listeningSockets: - for i in self.listeningSockets: + for i in self.listeningSockets.values(): i.handle_close() logger.info('Stopped listening for incoming connections.') if self.udpSockets: - for i in self.udpSockets: + for i in self.udpSockets.values(): i.handle_close() logger.info('Stopped udp sockets.') diff --git a/src/network/udp.py b/src/network/udp.py index 8448da16..27cf0abb 100644 --- a/src/network/udp.py +++ b/src/network/udp.py @@ -17,7 +17,7 @@ class UDPSocket(BMProto): port = 8444 announceInterval = 60 - def __init__(self, host=None, sock=None): + def __init__(self, host=None, sock=None, announcing=False): super(BMProto, self).__init__(sock=sock) self.verackReceived = True self.verackSent = True @@ -49,6 +49,7 @@ class UDPSocket(BMProto): ObjectTracker.__init__(self) self.connecting = False self.connected = True + self.announcing = announcing self.set_state("bm_header", expectBytes=protocol.Header.size) def set_socket_reuse(self): diff --git a/src/protocol.py b/src/protocol.py index c2a58de8..7ad0db17 100644 --- a/src/protocol.py +++ b/src/protocol.py @@ -37,6 +37,8 @@ OBJECT_GETPUBKEY = 0 OBJECT_PUBKEY = 1 OBJECT_MSG = 2 OBJECT_BROADCAST = 3 +OBJECT_I2P = 0x493250 +OBJECT_ADDR = 0x61646472 eightBytesOfRandomDataUsedToDetectConnectionsToSelf = pack( '>Q', random.randrange(1, 18446744073709551615)) @@ -108,7 +110,7 @@ def checkIPv4Address(host, hostStandardFormat, private=False): if host[0] == '\x7F': # 127/8 if not private: logger.debug('Ignoring IP address in loopback range: ' + hostStandardFormat) - return False + return hostStandardFormat if private else False if host[0] == '\x0A': # 10/8 if not private: logger.debug('Ignoring IP address in private range: ' + hostStandardFormat) From 6c695c8ac77842f02af25162b89ed250e725a584 Mon Sep 17 00:00:00 2001 From: Peter Surda Date: Wed, 9 Aug 2017 17:36:52 +0200 Subject: [PATCH 182/407] Remove non-asyncore network code (partial) --- src/api.py | 12 +----- src/bitmessagemain.py | 55 ++++++++++--------------- src/bmconfigparser.py | 1 - src/class_singleWorker.py | 36 +++------------- src/network/stats.py | 86 ++++++++++++++++----------------------- 5 files changed, 65 insertions(+), 125 deletions(-) diff --git a/src/api.py b/src/api.py index 7c67f89f..dcc83538 100644 --- a/src/api.py +++ b/src/api.py @@ -864,11 +864,7 @@ class MySimpleXMLRPCRequestHandler(SimpleXMLRPCRequestHandler): objectType, toStreamNumber, encryptedPayload, int(time.time()) + TTL,'') with shared.printLock: print 'Broadcasting inv for msg(API disseminatePreEncryptedMsg command):', hexlify(inventoryHash) - if BMConfigParser().get("network", "asyncore"): - queues.invQueue.put((toStreamNumber, inventoryHash)) - else: - protocol.broadcastToSendDataQueues(( - toStreamNumber, 'advertiseobject', inventoryHash)) + queues.invQueue.put((toStreamNumber, inventoryHash)) def HandleTrashSentMessageByAckDAta(self, params): # This API method should only be used when msgid is not available @@ -914,11 +910,7 @@ class MySimpleXMLRPCRequestHandler(SimpleXMLRPCRequestHandler): objectType, pubkeyStreamNumber, payload, int(time.time()) + TTL,'') with shared.printLock: print 'broadcasting inv within API command disseminatePubkey with hash:', hexlify(inventoryHash) - if BMConfigParser().get("network", "asyncore"): - queues.invQueue.put((pubkeyStreamNumber, inventoryHash)) - else: - protocol.broadcastToSendDataQueues(( - pubkeyStreamNumber, 'advertiseobject', inventoryHash)) + queues.invQueue.put((pubkeyStreamNumber, inventoryHash)) def HandleGetMessageDataByDestinationHash(self, params): # Method will eventually be used by a particular Android app to diff --git a/src/bitmessagemain.py b/src/bitmessagemain.py index 17e97ffd..eed725d3 100755 --- a/src/bitmessagemain.py +++ b/src/bitmessagemain.py @@ -92,13 +92,7 @@ def connectToStream(streamNumber): if streamNumber*2+1 not in knownnodes.knownNodes: knownnodes.knownNodes[streamNumber*2+1] = {} - if BMConfigParser().get("network", "asyncore"): - BMConnectionPool().connectToStream(streamNumber) - else: - for i in range(state.maximumNumberOfHalfOpenConnections): - a = outgoingSynSender() - a.setup(streamNumber, selfInitiatedConnections) - a.start() + BMConnectionPool().connectToStream(streamNumber) def _fixSocket(): if sys.platform.startswith('linux'): @@ -281,36 +275,29 @@ class Main: singleAPIThread.daemon = True # close the main program even if there are threads left singleAPIThread.start() - if BMConfigParser().get("network", "asyncore"): - BMConnectionPool() - asyncoreThread = BMNetworkThread() - asyncoreThread.daemon = True - asyncoreThread.start() - for i in range(BMConfigParser().getint("threads", "receive")): - receiveQueueThread = ReceiveQueueThread(i) - receiveQueueThread.daemon = True - receiveQueueThread.start() - announceThread = AnnounceThread() - announceThread.daemon = True - announceThread.start() - state.invThread = InvThread() - state.invThread.daemon = True - state.invThread.start() - state.addrThread = AddrThread() - state.addrThread.daemon = True - state.addrThread.start() - state.downloadThread = DownloadThread() - state.downloadThread.daemon = True - state.downloadThread.start() + BMConnectionPool() + asyncoreThread = BMNetworkThread() + asyncoreThread.daemon = True + asyncoreThread.start() + for i in range(BMConfigParser().getint("threads", "receive")): + receiveQueueThread = ReceiveQueueThread(i) + receiveQueueThread.daemon = True + receiveQueueThread.start() + announceThread = AnnounceThread() + announceThread.daemon = True + announceThread.start() + state.invThread = InvThread() + state.invThread.daemon = True + state.invThread.start() + state.addrThread = AddrThread() + state.addrThread.daemon = True + state.addrThread.start() + state.downloadThread = DownloadThread() + state.downloadThread.daemon = True + state.downloadThread.start() connectToStream(1) - if not BMConfigParser().get("network", "asyncore"): - singleListenerThread = singleListener() - singleListenerThread.setup(selfInitiatedConnections) - singleListenerThread.daemon = True # close the main program even if there are threads left - singleListenerThread.start() - if BMConfigParser().safeGetBoolean('bitmessagesettings','upnp'): import upnp upnpThread = upnp.uPnPThread() diff --git a/src/bmconfigparser.py b/src/bmconfigparser.py index b8bf790f..acc4476a 100644 --- a/src/bmconfigparser.py +++ b/src/bmconfigparser.py @@ -19,7 +19,6 @@ BMConfigDefaults = { "receive": 3, }, "network": { - "asyncore": True, "bind": '', }, "inventory": { diff --git a/src/class_singleWorker.py b/src/class_singleWorker.py index 07c768a4..e7f58f1b 100644 --- a/src/class_singleWorker.py +++ b/src/class_singleWorker.py @@ -192,11 +192,7 @@ class singleWorker(threading.Thread, StoppableThread): logger.info('broadcasting inv with hash: ' + hexlify(inventoryHash)) - if BMConfigParser().get("network", "asyncore"): - queues.invQueue.put((streamNumber, inventoryHash)) - else: - protocol.broadcastToSendDataQueues(( - streamNumber, 'advertiseobject', inventoryHash)) + queues.invQueue.put((streamNumber, inventoryHash)) queues.UISignalQueue.put(('updateStatusBar', '')) try: BMConfigParser().set( @@ -286,11 +282,7 @@ class singleWorker(threading.Thread, StoppableThread): logger.info('broadcasting inv with hash: ' + hexlify(inventoryHash)) - if BMConfigParser().get("network", "asyncore"): - queues.invQueue.put((streamNumber, inventoryHash)) - else: - protocol.broadcastToSendDataQueues(( - streamNumber, 'advertiseobject', inventoryHash)) + queues.invQueue.put((streamNumber, inventoryHash)) queues.UISignalQueue.put(('updateStatusBar', '')) try: BMConfigParser().set( @@ -380,11 +372,7 @@ class singleWorker(threading.Thread, StoppableThread): logger.info('broadcasting inv with hash: ' + hexlify(inventoryHash)) - if BMConfigParser().get("network", "asyncore"): - queues.invQueue.put((streamNumber, inventoryHash)) - else: - protocol.broadcastToSendDataQueues(( - streamNumber, 'advertiseobject', inventoryHash)) + queues.invQueue.put((streamNumber, inventoryHash)) queues.UISignalQueue.put(('updateStatusBar', '')) try: BMConfigParser().set( @@ -513,11 +501,7 @@ class singleWorker(threading.Thread, StoppableThread): objectType, streamNumber, payload, embeddedTime, tag) PendingUpload().add(inventoryHash) logger.info('sending inv (within sendBroadcast function) for object: ' + hexlify(inventoryHash)) - if BMConfigParser().get("network", "asyncore"): - queues.invQueue.put((streamNumber, inventoryHash)) - else: - protocol.broadcastToSendDataQueues(( - streamNumber, 'advertiseobject', inventoryHash)) + queues.invQueue.put((streamNumber, inventoryHash)) queues.UISignalQueue.put(('updateSentItemStatusByAckdata', (ackdata, tr._translate("MainWindow", "Broadcast sent on %1").arg(l10n.formatTimestamp())))) @@ -846,11 +830,7 @@ class singleWorker(threading.Thread, StoppableThread): # not sending to a chan or one of my addresses queues.UISignalQueue.put(('updateSentItemStatusByAckdata', (ackdata, tr._translate("MainWindow", "Message sent. Waiting for acknowledgement. Sent on %1").arg(l10n.formatTimestamp())))) logger.info('Broadcasting inv for my msg(within sendmsg function):' + hexlify(inventoryHash)) - if BMConfigParser().get("network", "asyncore"): - queues.invQueue.put((toStreamNumber, inventoryHash)) - else: - protocol.broadcastToSendDataQueues(( - toStreamNumber, 'advertiseobject', inventoryHash)) + queues.invQueue.put((toStreamNumber, inventoryHash)) # Update the sent message in the sent table with the necessary information. if BMConfigParser().has_section(toaddress) or not protocol.checkBitfield(behaviorBitfield, protocol.BITFIELD_DOESACK): @@ -952,11 +932,7 @@ class singleWorker(threading.Thread, StoppableThread): objectType, streamNumber, payload, embeddedTime, '') PendingUpload().add(inventoryHash) logger.info('sending inv (for the getpubkey message)') - if BMConfigParser().get("network", "asyncore"): - queues.invQueue.put((streamNumber, inventoryHash)) - else: - protocol.broadcastToSendDataQueues(( - streamNumber, 'advertiseobject', inventoryHash)) + queues.invQueue.put((streamNumber, inventoryHash)) # wait 10% past expiration sleeptill = int(time.time() + TTL * 1.1) diff --git a/src/network/stats.py b/src/network/stats.py index 7f7c83ac..45961ac1 100644 --- a/src/network/stats.py +++ b/src/network/stats.py @@ -15,67 +15,53 @@ lastSentBytes = 0 currentSentSpeed = 0 def connectedHostsList(): - if BMConfigParser().get("network", "asyncore"): - retval = [] - for i in BMConnectionPool().inboundConnections.values() + BMConnectionPool().outboundConnections.values(): - if not i.fullyEstablished: - continue - try: - retval.append(i) - except AttributeError: - pass - return retval - return shared.connectedHostsList.items() + retval = [] + for i in BMConnectionPool().inboundConnections.values() + BMConnectionPool().outboundConnections.values(): + if not i.fullyEstablished: + continue + try: + retval.append(i) + except AttributeError: + pass + return retval def sentBytes(): - if BMConfigParser().get("network", "asyncore"): - return asyncore.sentBytes - return throttle.SendThrottle().total + return asyncore.sentBytes def uploadSpeed(): global lastSentTimestamp, lastSentBytes, currentSentSpeed - if BMConfigParser().get("network", "asyncore"): - currentTimestamp = time.time() - if int(lastSentTimestamp) < int(currentTimestamp): - currentSentBytes = asyncore.sentBytes - currentSentSpeed = int((currentSentBytes - lastSentBytes) / (currentTimestamp - lastSentTimestamp)) - lastSentBytes = currentSentBytes - lastSentTimestamp = currentTimestamp - return currentSentSpeed - return throttle.sendThrottle().getSpeed() + currentTimestamp = time.time() + if int(lastSentTimestamp) < int(currentTimestamp): + currentSentBytes = asyncore.sentBytes + currentSentSpeed = int((currentSentBytes - lastSentBytes) / (currentTimestamp - lastSentTimestamp)) + lastSentBytes = currentSentBytes + lastSentTimestamp = currentTimestamp + return currentSentSpeed def receivedBytes(): - if BMConfigParser().get("network", "asyncore"): - return asyncore.receivedBytes - return throttle.ReceiveThrottle().total + return asyncore.receivedBytes def downloadSpeed(): global lastReceivedTimestamp, lastReceivedBytes, currentReceivedSpeed - if BMConfigParser().get("network", "asyncore"): - currentTimestamp = time.time() - if int(lastReceivedTimestamp) < int(currentTimestamp): - currentReceivedBytes = asyncore.receivedBytes - currentReceivedSpeed = int((currentReceivedBytes - lastReceivedBytes) / (currentTimestamp - lastReceivedTimestamp)) - lastReceivedBytes = currentReceivedBytes - lastReceivedTimestamp = currentTimestamp - return currentReceivedSpeed - return throttle.ReceiveThrottle().getSpeed() + currentTimestamp = time.time() + if int(lastReceivedTimestamp) < int(currentTimestamp): + currentReceivedBytes = asyncore.receivedBytes + currentReceivedSpeed = int((currentReceivedBytes - lastReceivedBytes) / (currentTimestamp - lastReceivedTimestamp)) + lastReceivedBytes = currentReceivedBytes + lastReceivedTimestamp = currentTimestamp + return currentReceivedSpeed def pendingDownload(): - if BMConfigParser().get("network", "asyncore"): - tmp = {} - for connection in BMConnectionPool().inboundConnections.values() + BMConnectionPool().outboundConnections.values(): - for k in connection.objectsNewToMe.keys(): - tmp[k] = True - return len(tmp) - return PendingDownloadQueue.totalSize() + tmp = {} + for connection in BMConnectionPool().inboundConnections.values() + BMConnectionPool().outboundConnections.values(): + for k in connection.objectsNewToMe.keys(): + tmp[k] = True + return len(tmp) def pendingUpload(): - if BMConfigParser().get("network", "asyncore"): - return 0 - tmp = {} - for connection in BMConnectionPool().inboundConnections.values() + BMConnectionPool().outboundConnections.values(): - for k in connection.objectsNewToThem.keys(): - tmp[k] = True - return len(tmp) - return PendingUpload().len() + return 0 + tmp = {} + for connection in BMConnectionPool().inboundConnections.values() + BMConnectionPool().outboundConnections.values(): + for k in connection.objectsNewToThem.keys(): + tmp[k] = True + return len(tmp) From e7231f3aead4d10a49dbb46092b4204fee598e2e Mon Sep 17 00:00:00 2001 From: Peter Surda Date: Wed, 9 Aug 2017 23:30:22 +0200 Subject: [PATCH 183/407] Fix multiple TCP bind address handling --- src/network/connectionpool.py | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/src/network/connectionpool.py b/src/network/connectionpool.py index 3adc1772..1026d305 100644 --- a/src/network/connectionpool.py +++ b/src/network/connectionpool.py @@ -116,11 +116,12 @@ class BMConnectionPool(object): host = BMConfigParser().get("network", "bind") return host - def startListening(self): - host = self.getListeningIP() + def startListening(self, bind=None): + if bind is None: + bind = self.getListeningIP() port = BMConfigParser().safeGetInt("bitmessagesettings", "port") # correct port even if it changed - ls = network.tcp.TCPServer(host=host, port=port) + ls = network.tcp.TCPServer(host=bind, port=port) self.listeningSockets[ls.destination] = ls def startUDPSocket(self, bind=None): @@ -192,7 +193,11 @@ class BMConnectionPool(object): if acceptConnections: if not self.listeningSockets: - self.startListening() + if BMConfigParser().safeGet("network", "bind") == '': + self.startListening() + else: + for bind in re.sub("[^\w.]+", " ", BMConfigParser().safeGet("network", "bind")).split(): + self.startListening(bind) logger.info('Listening for incoming connections.') if not self.udpSockets: if BMConfigParser().safeGet("network", "bind") == '': From 85e4e5438ca942d3d9ac5b6d3938adad711e6067 Mon Sep 17 00:00:00 2001 From: PyBitmessage Translations Date: Fri, 11 Aug 2017 09:38:00 +0200 Subject: [PATCH 184/407] Auto-updated language de from transifex --- src/translations/bitmessage_de.qm | Bin 96084 -> 97437 bytes src/translations/bitmessage_de.ts | 356 +++++++++++++++++------------- 2 files changed, 200 insertions(+), 156 deletions(-) diff --git a/src/translations/bitmessage_de.qm b/src/translations/bitmessage_de.qm index 4a74855d7a47ec4a283f69ca93a1174c3d40a899..6bef4d31ab079830dc5d9da8359206caf3d17d48 100644 GIT binary patch delta 4810 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 MY-SZdp5^uZFD#l{q5uE@ diff --git a/src/translations/bitmessage_de.ts b/src/translations/bitmessage_de.ts index d9c57fd2..99505de8 100644 --- a/src/translations/bitmessage_de.ts +++ b/src/translations/bitmessage_de.ts @@ -27,7 +27,7 @@ Register on email gateway - An E-Mailschnittstelle registrieren + An E-Mail Schnittstelle registrieren @@ -58,12 +58,12 @@ EmailGatewayRegistrationDialog - + Registration failed: Registrierung fehlgeschlagen: - + The requested email address is not available, please try a new one. Fill out the new desired email address (including @mailchuck.com) below: Die gewünschte E-Mailaddresse ist nicht verfügbar, bitte probieren Sie eine neue. Die gewünschte E-Mailaddresse (inkl. @mailchuck.com) unten ausfüllen: @@ -324,7 +324,7 @@ Bitte geben Sie die gewünschte E-Mail-Adresse (inkl. @mailchuck.com) unten ein: Bestätigung der Nachricht erhalten %1 - + Broadcast queued. Rundruf in Warteschlange. @@ -354,7 +354,7 @@ Bitte geben Sie die gewünschte E-Mail-Adresse (inkl. @mailchuck.com) unten ein: Unbekannter Status: %1 %2 - + Not Connected Nicht verbunden @@ -386,7 +386,7 @@ Bitte geben Sie die gewünschte E-Mail-Adresse (inkl. @mailchuck.com) unten ein: You may manage your keys by editing the keys.dat file stored in the same directory as this program. It is important that you back up this file. - Sie können Ihre Schlüssel verwalten, indem Sie die keys.dat bearbeiten, die im gleichen Ordner wie das Programm liegt. Es ist empfehlenswert, vorher ein Backup dieser Datei anlegen. + Sie können Ihre Schlüssel verwalten, indem Sie die keys.dat bearbeiten, die im gleichen Ordner wie das Programm liegt. Es ist empfehlenswert, vorher ein Backup dieser Datei anzulegen. @@ -395,7 +395,7 @@ Bitte geben Sie die gewünschte E-Mail-Adresse (inkl. @mailchuck.com) unten ein: It is important that you back up this file. Sie können Ihre Schlüssel verwalten, indem Sie die keys.dat bearbeiten, die im Ordner %1 liegt. -Es ist empfehlenswert, vorher ein Backup dieser Datei anlegen. +Es ist empfehlenswert, vorher ein Backup dieser Datei anzulegen. @@ -405,7 +405,7 @@ Es ist empfehlenswert, vorher ein Backup dieser Datei anlegen. You may manage your keys by editing the keys.dat file stored in the same directory as this program. It is important that you back up this file. Would you like to open the file now? (Be sure to close Bitmessage before making any changes.) - Sie können Ihre Schlüssel verwalten, indem Sie die keys.dat bearbeiten, die im gleichen Ordner wie das Programm liegt. Es ist empfehlenswert, vorher ein Backup dieser Datei anlegen. Möchten Sie die Datei jetzt öffnen? (Stellen Sie sicher, dass Sie Bitmessage beendet haben, bevor Sie etwas ändern.) + Sie können Ihre Schlüssel verwalten, indem Sie die keys.dat bearbeiten, die im gleichen Ordner wie das Programm liegt. Es ist empfehlenswert, vorher ein Backup dieser Datei anzulegen. Möchten Sie die Datei jetzt öffnen? (Stellen Sie sicher, dass Sie Bitmessage beendet haben, bevor Sie etwas ändern.) @@ -414,7 +414,7 @@ Es ist empfehlenswert, vorher ein Backup dieser Datei anlegen. It is important that you back up this file. Would you like to open the file now? (Be sure to close Bitmessage before making any changes.) Sie können Ihre Schlüssel verwalten, indem Sie die keys.dat bearbeiten, die im Ordner %1 liegt. -Es ist empfehlenswert, vorher ein Backup dieser Datei anlegen. Möchten Sie die Datei jetzt öffnen? +Es ist empfehlenswert, vorher ein Backup dieser Datei anzulegen. Möchten Sie die Datei jetzt öffnen? (Stellen Sie sicher, dass Sie Bitmessage beendet haben, bevor Sie etwas ändern.) @@ -518,22 +518,22 @@ Es ist empfehlenswert, vorher ein Backup dieser Datei anlegen. Möchten Sie die - + Connection lost Verbindung verloren - + Connected Verbunden - + Message trashed Nachricht in den Papierkorb verschoben - + The TTL, or Time-To-Live is the length of time that the network will hold the message. The recipient must get it during this time. If your Bitmessage client does not hear an acknowledgement, it will resend the message automatically. The longer the Time-To-Live, the @@ -541,19 +541,19 @@ Es ist empfehlenswert, vorher ein Backup dieser Datei anlegen. Möchten Sie die Die Haltbarkeit, oder Time-To-Live, ist die Dauer, für die das Netzwerk die Nachricht speichern wird. Der Empfänger muss sie während dieser Zeit empfangen. Wenn Ihr Bitmessage-Client keine Empfangsbestätigung erhält, wird die Nachricht automatisch erneut verschickt. Je länger die Time-To-Live, desto mehr Arbeit muss Ihr Rechner verrichten, um die Nachricht zu senden. Eine Time-To-Live von vier oder fünf Tagen ist meist ausreichend. - + Message too long Narchricht zu lang - + The message that you are trying to send is too long by %1 bytes. (The maximum is 261644 bytes). Please cut it down before sending. Die Nachricht, die Sie zu senden versuchen, ist %1 Byte zu lang. (Maximum 261.644 Bytes). Bitte verringern Sie ihre Größe vor dem Senden. - + Error: Your account wasn't registered at an email gateway. Sending registration now as %1, please wait for the registration to be processed before retrying sending. - Fehler: Ihr Konto war an keiner E-Mailschnittstelle registriert. Registrierung als %1 wird versandt, bitte vor einem erneutem Sendeversuch auf die Registrierungsverarbeitung warten. + Fehler: Ihr Konto war an keiner E-Mail Schnittstelle registriert. Registrierung als %1 wird versandt, bitte vor einem erneutem Sendeversuch auf die Registrierungsverarbeitung warten. @@ -596,67 +596,67 @@ Es ist empfehlenswert, vorher ein Backup dieser Datei anlegen. Möchten Sie die - + Error: You must specify a From address. If you don't have one, go to the 'Your Identities' tab. Fehler: Sie müssen eine Absenderadresse auswählen. Sollten Sie keine haben, wechseln Sie zum Reiter "Ihre Identitäten". - + Address version number Adressversion - + Concerning the address %1, Bitmessage cannot understand address version numbers of %2. Perhaps upgrade Bitmessage to the latest version. Aufgrund der Adresse %1 kann Bitmessage Adressen mit der Version %2 nicht verarbeiten. Möglicherweise müssen Sie Bitmessage auf die aktuelle Version aktualisieren. - + Stream number Datenstrom Nummer - + Concerning the address %1, Bitmessage cannot handle stream numbers of %2. Perhaps upgrade Bitmessage to the latest version. Aufgrund der Adresse %1 kann Bitmessage den Datenstrom mit der Version %2 nicht verarbeiten. Möglicherweise müssen Sie Bitmessage auf die aktuelle Version aktualisieren. - + Warning: You are currently not connected. Bitmessage will do the work necessary to send the message but it won't send until you connect. Warnung: Sie sind aktuell nicht verbunden. Bitmessage wird die nötige Arbeit zum versenden verrichten, aber erst senden, wenn Sie verbunden sind. - + Message queued. Nachricht befindet sich in der Warteschleife. - + Your 'To' field is empty. Ihr "Empfänger"-Feld ist leer. - + Right click one or more entries in your address book and select 'Send message to this address'. Klicken Sie mit rechts auf einen oder mehrere Einträge aus Ihrem Adressbuch und wählen Sie "Nachricht an diese Adresse senden". - + Fetched address from namecoin identity. Adresse aus Namecoin Identität geholt. - + New Message Neue Nachricht - + From Von - + Sending email gateway registration request Der Registrierungsantrag für die E-Mail Schnittstelle wird versandt. @@ -671,142 +671,142 @@ Es ist empfehlenswert, vorher ein Backup dieser Datei anlegen. Möchten Sie die Die von Ihnen eingegebene Adresse ist ungültig, sie wird ignoriert. - + Error: You cannot add the same address to your address book twice. Try renaming the existing one if you want. Fehler: Sie können eine Adresse nicht doppelt im Adressbuch speichern. Sie können jedoch die bereits eingetragene umbenennen. - + Error: You cannot add the same address to your subscriptions twice. Perhaps rename the existing one if you want. Fehler: Dieselbe Adresse kann nicht doppelt in die Abonnements eingetragen werden. Sie können jedoch die bereits eingetragene umbenennen. - + Restart Neustart - + You must restart Bitmessage for the port number change to take effect. Sie müssen Bitmessage neu starten, um den geänderten Port zu verwenden. - + Bitmessage will use your proxy from now on but you may want to manually restart Bitmessage now to close existing connections (if any). Bitmessage wird ab sofort den Proxy-Server verwenden, aber eventuell möchten Sie Bitmessage neu starten um bereits bestehende Verbindungen zu schließen. - + Number needed Zahl erforderlich - + Your maximum download and upload rate must be numbers. Ignoring what you typed. Ihre maximale Herungerlade- und Hochladegeschwindigkeit müssen Zahlen sein. Die eingetragenen Werte werden ignoriert. - + Will not resend ever Wird nie wiederversendet - + Note that the time limit you entered is less than the amount of time Bitmessage waits for the first resend attempt therefore your messages will never be resent. Bitte beachten Sie, dass der eingetratene Dauer kürzer ist als die, die Bitmessage auf das erste Wiederversenden wartet. Deswegen werden Ihre Nachrichten nie wiederversendet. - + Sending email gateway unregistration request E-Mail Schnittestellen-Abmeldeantrag wird versandt - + Sending email gateway status request E-Mail Schnittestellen Statusantrag wird versandt - + Passphrase mismatch Kennwort stimmt nicht überein - + The passphrase you entered twice doesn't match. Try again. Die von Ihnen eingegebenen Kennwörter sind nicht identisch. Bitte neu versuchen. - + Choose a passphrase Wählen Sie ein Kennwort - + You really do need a passphrase. Sie benötigen wirklich ein Kennwort. - + Address is gone Adresse ist verloren - + Bitmessage cannot find your address %1. Perhaps you removed it? Bitmessage kann Ihre Adresse %1 nicht finden. Haben Sie sie gelöscht? - + Address disabled Adresse deaktiviert - + Error: The address from which you are trying to send is disabled. You'll have to enable it on the 'Your Identities' tab before using it. Fehler: Die Adresse von der Sie versuchen zu senden ist deaktiviert. Sie müssen sie unter dem Reiter "Ihre Identitäten" aktivieren bevor Sie fortfahren. - + Entry added to the Address Book. Edit the label to your liking. Eintrag dem Adressbuch hinzugefügt. Editieren Sie den Eintrag nach Belieben. - + Entry added to the blacklist. Edit the label to your liking. Eintrag in die Blacklist hinzugefügt. Die Beschriftung können Sie ändern. - + Error: You cannot add the same address to your blacklist twice. Try renaming the existing one if you want. Fehler: Dieselbe Addresse kann nicht doppelt in die Blacklist eingetragen werden. Sie können jedoch die bereits eingetragene umbenennen. - + Moved items to trash. Objekt(e) in den Papierkorb verschoben. - + Undeleted item. Nachricht wiederhergestellt. - + Save As... Speichern unter... - + Write error. Fehler beim Speichern. - + No addresses selected. Keine Adresse ausgewählt. - + If you delete the subscription, messages that you already received will become inaccessible. Maybe you can consider disabling the subscription instead. Disabled subscriptions will not receive new messages, but you can still view messages you already received. Are you sure you want to delete the subscription? @@ -815,7 +815,7 @@ Are you sure you want to delete the subscription? Sind Sie sicher, dass Sie das Abonnement löschen möchten? - + If you delete the channel, messages that you already received will become inaccessible. Maybe you can consider disabling the channel instead. Disabled channels will not receive new messages, but you can still view messages you already received. Are you sure you want to delete the channel? @@ -824,92 +824,92 @@ Are you sure you want to delete the channel? Sind Sie sicher, dass Sie das Chan löschen möchten? - + Do you really want to remove this avatar? Wollen Sie diesen Avatar wirklich entfernen? - + You have already set an avatar for this address. Do you really want to overwrite it? Sie haben bereits einen Avatar für diese Adresse gewählt. Wollen Sie ihn wirklich überschreiben? - + Start-on-login not yet supported on your OS. Mit Betriebssystem starten, noch nicht von Ihrem Betriebssystem unterstützt - + Minimize-to-tray not yet supported on your OS. Ins System Tray minimieren von Ihrem Betriebssytem noch nicht unterstützt. - + Tray notifications not yet supported on your OS. Trach-Benachrichtigungen von Ihrem Betriebssystem noch nicht unterstützt. - + Testing... teste... - + This is a chan address. You cannot use it as a pseudo-mailing list. Dies ist eine Chan-Adresse. Sie können sie nicht als Pseudo-Mailingliste verwenden. - + The address should start with ''BM-'' Die Adresse sollte mit "BM-" beginnen - + The address is not typed or copied correctly (the checksum failed). Die Adresse wurde nicht korrekt getippt oder kopiert (Prüfsumme falsch). - + The version number of this address is higher than this software can support. Please upgrade Bitmessage. Die Versionsnummer dieser Adresse ist höher als diese Software unterstützt. Bitte installieren Sie die neuste Bitmessage Version. - + The address contains invalid characters. Diese Adresse beinhaltet ungültige Zeichen. - + Some data encoded in the address is too short. Die in der Adresse codierten Daten sind zu kurz. - + Some data encoded in the address is too long. Die in der Adresse codierten Daten sind zu lang. - + Some data encoded in the address is malformed. Einige in der Adresse kodierten Daten sind ungültig. - + Enter an address above. Eine Addresse oben ausfüllen. - + Address is an old type. We cannot display its past broadcasts. Alter Addressentyp. Wir können deren vorige Rundrufe nicht anzeigen. - + There are no recent broadcasts from this address to display. Es gibt keine neuen Rundrufe von dieser Adresse die angezeigt werden können. - + You are using TCP port %1. (This can be changed in the settings). Sie benutzen TCP-Port %1 (Dieser kann in den Einstellungen verändert werden). @@ -1016,7 +1016,7 @@ Sind Sie sicher, dass Sie das Chan löschen möchten? TTL: - Haltbarkeit: + Lebenszeit: @@ -1119,47 +1119,47 @@ Sind Sie sicher, dass Sie das Chan löschen möchten? Neuen Eintrag erstellen - + Display the %1 recent broadcast(s) from this address. Die letzten %1 Rundruf(e) von dieser Addresse anzeigen. - + New version of PyBitmessage is available: %1. Download it from https://github.com/Bitmessage/PyBitmessage/releases/latest Neue Version von PyBitmessage steht zur Verfügung: %1. Sie können sie von https://github.com/Bitmessage/PyBitmessage/releases/latest herunterladen. - + Waiting for PoW to finish... %1% Warte auf Abschluss von Berechnungen (PoW)... %1% - + Shutting down Pybitmessage... %1% PyBitmessage wird beendet... %1% - + Waiting for objects to be sent... %1% Warte auf Versand von Objekten... %1% - + Saving settings... %1% Einstellungen werden gespeichert... %1% - + Shutting down core... %1% Kern wird beendet... %1% - + Stopping notifications... %1% Beende Benachrichtigungen... %1% - + Shutdown imminent... %1% Unmittelbar vor Beendung... %1% @@ -1174,7 +1174,7 @@ Sind Sie sicher, dass Sie das Chan löschen möchten? %n Tag%n Tage - + Shutting down PyBitmessage... %1% PyBitmessage wird beendet... %1% @@ -1224,60 +1224,60 @@ Sind Sie sicher, dass Sie das Chan löschen möchten? Warnung: Datenträger ist voll. Bitmessage wird jetzt beendet. - + Error! Could not find sender address (your address) in the keys.dat file. Fehler! Konnte die Absenderadresse (Ihre Adresse) in der keys.dat-Datei nicht finden. - + Doing work necessary to send broadcast... Arbeit wird verrichtet, um Rundruf zu verschicken... - + Broadcast sent on %1 Rundruf verschickt um %1 - + Encryption key was requested earlier. Verschlüsselungscode wurde früher angefordert. - + Sending a request for the recipient's encryption key. Anfrage nach dem Verschlüsselungscode des Empfängers wird versendet. - + Looking up the receiver's public key Suche nach dem öffentlichen Schlüssel des Empfängers - + Problem: Destination is a mobile device who requests that the destination be included in the message but this is disallowed in your settings. %1 Problem: Der Empfänger benutzt ein mobiles Gerät und erfordert eine unverschlüsselte Empfängeraddresse. Dies ist in Ihren Einstellungen jedoch nicht zulässig. 1% - + Doing work necessary to send message. There is no required difficulty for version 2 addresses like this. Arbeit für Nachrichtenversand wird verrichtet. Version-2-Addressen wie die des Empfängers haben keine Schweirigkeitserforderungen. - + Doing work necessary to send message. Receiver's required difficulty: %1 and %2 Arbeit für Nachrichtenversand wird errichtet. Vom Empfänger geforderte Schwierigkeit: %1 und %2 - + Problem: The work demanded by the recipient (%1 and %2) is more difficult than you are willing to do. %3 Problem: Die vom Empfänger verlangte Arbeit (%1 und %2) ist schwieriger, als Sie in den Einstellungen erlaubt haben. %3 - + Problem: You are trying to send a message to yourself or a chan but your encryption key could not be found in the keys.dat file. Could not encrypt message. %1 Problem: Sie versuchen, eine Nachricht an sich zu versenden, aber Ihr Schlüssel befindet sich nicht in der keys.dat-Datei. Die Nachricht kann nicht verschlüsselt werden. 1% @@ -1287,7 +1287,7 @@ Receiver's required difficulty: %1 and %2 Arbeit wird verrichtet, um die Nachricht zu verschicken. - + Message sent. Waiting for acknowledgement. Sent on %1 Nachricht gesendet. Auf Bestätigung wird gewartet. Zeitpunkt der Sendung: %1 @@ -1297,22 +1297,22 @@ Receiver's required difficulty: %1 and %2 Arbeit wird verrichtet, um den Schlüssel nachzufragen... - + Broadcasting the public key request. This program will auto-retry if they are offline. Anfrage nach dem öffentlichen Schlüssel läuft. Wenn der Besitzer nicht mit dem Netzwerk verbunden ist, wird ein Wiederholungsversuch unternommen. - + Sending public key request. Waiting for reply. Requested at %1 Nachfrage nach dem öffentlichen Schlüssel läuft, auf Antwort wird gewartet. Nachgefragt am %1 - + UPnP port mapping established on port %1 UPnP Port-Mapping eingerichtet auf Port %1 - + UPnP port mapping removed UPnP Port-Mapping entfernt @@ -1322,7 +1322,7 @@ Receiver's required difficulty: %1 and %2 Alle Nachrichten als gelesen markieren - + Are you sure you would like to mark all messages read? Sind Sie sicher, dass Sie alle Nachrichten als gelesen markieren möchten? @@ -1332,37 +1332,37 @@ Receiver's required difficulty: %1 and %2 Führe Arbeit aus, die notwendig ist zum Senden des Rundspruches. - + Proof of work pending Arbeitsbeweis wird berechnet - + %n object(s) pending proof of work %n Objekt wartet auf Berechnungen%n Objekte warten auf Berechnungen - + %n object(s) waiting to be distributed %n Objekt wartet darauf, verteilt zu werden%n Objekte warten darauf, verteilt zu werden - + Wait until these tasks finish? Warten bis diese Aufgaben erledigt sind? - + Problem communicating with proxy: %1. Please check your network settings. Kommunikationsfehler mit dem Proxy: %1. Bitte überprüfen Sie Ihre Netzwerkeinstellungen. - + SOCKS5 Authentication problem: %1. Please check your SOCKS5 settings. SOCKS5-Authentizierung fehlgeschlagen: %1. Bitte überprüfen Sie Ihre SOCKS5-Einstellungen. - + The time on your computer, %1, may be wrong. Please verify your settings. Die Uhrzeit ihres Computers, %1, ist möglicherweise falsch. Bitte überprüfen Sie Ihre einstellungen. @@ -1420,11 +1420,10 @@ Receiver's required difficulty: %1 and %2 * discuss in chan(nel)s with other people -Wilkommen zu einfacher und sicherer Bitmessage +Willkommen zu einfachem und sicherem Bitmessage * senden Sie Nachrichten an andere Leute -* senden Sie Rundruf-Nachrichte wie bei Twitter oder -* diskutieren Sie mit anderen Leuten in Chans - +* senden Sie Rundrufe wie bei Twitter oder +* diskutieren Sie mit anderen Leuten in Chans @@ -1432,77 +1431,77 @@ Wilkommen zu einfacher und sicherer Bitmessage für Chans nicht empfohlen - + Problems connecting? Try enabling UPnP in the Network Settings Verbindungsprobleme? Versuchen Sie UPnP in den Netzwerkeinstellungen einzuschalten - + Error: Bitmessage addresses start with BM- Please check the recipient address %1 Fehler: Bitmessage Adressen starten mit BM- Bitte überprüfen Sie die Empfängeradresse %1 - + Error: The recipient address %1 is not typed or copied correctly. Please check it. Fehler: Die Empfängeradresse %1 wurde nicht korrekt getippt oder kopiert. Bitte überprüfen. - + Error: The recipient address %1 contains invalid characters. Please check it. Fehler: Die Empfängeradresse %1 beinhaltet ungültig Zeichen. Bitte überprüfen. - + Error: The version of the recipient address %1 is too high. Either you need to upgrade your Bitmessage software or your acquaintance is being clever. Fehler: Die Empfängerdresseversion von %1 ist zu hoch. Entweder Sie müssen Ihre Bitmessage Software aktualisieren oder Ihr Bekannter ist sehr clever. - + Error: Some data encoded in the recipient address %1 is too short. There might be something wrong with the software of your acquaintance. Fehler: Einige Daten die in der Empfängerdresse %1 codiert sind, sind zu kurz. Es könnte sein, dass etwas mit der Software Ihres Bekannten nicht stimmt. - + Error: Some data encoded in the recipient address %1 is too long. There might be something wrong with the software of your acquaintance. Fehler: Einige Daten die in der Empfängeradresse %1 codiert sind, sind zu lang. Es könnte sein, dass etwas mit der Software Ihres Bekannten nicht stimmt. - + Error: Some data encoded in the recipient address %1 is malformed. There might be something wrong with the software of your acquaintance. Fehler: Einige codierte Daten in der Empfängeradresse %1 sind ungültig. Es könnte etwas mit der Software Ihres Bekannten sein. - + Error: Something is wrong with the recipient address %1. Fehler: Mit der Empfängeradresse %1 stimmt etwas nicht. - + Synchronisation pending Synchronisierung läuft - + Bitmessage hasn't synchronised with the network, %n object(s) to be downloaded. If you quit now, it may cause delivery delays. Wait until the synchronisation finishes? Bitmessage ist nicht synchronisiert, %n Objekt wurde noch nicht heruntergeladen. Wenn Sie das Programm jetzt beenden, kann es zu Zustellverzögerungen kommen. Warten bis die Synchronisierung abgeschlossen ist?Bitmessage ist nicht synchronisiert, %n Objekte wurden noch nicht heruntergeladen. Wenn Sie das Programm jetzt beenden, kann es zu Zustellverzögerungen kommen. Warten bis die Synchronisierung abgeschlossen ist? - + Not connected Nicht verbunden - + Bitmessage isn't connected to the network. If you quit now, it may cause delivery delays. Wait until connected and the synchronisation finishes? Bitmessage ist nicht mit dem Netzwerk verbunden. Wenn Sie das Programm jetzt beenden, kann es zu Zustellverzögerungen kommen. Warten bis eine Verbindung besteht und die Synchronisierung abgeschlossen ist? - + Waiting for network connection... Warte auf Netzwerkverbindung... - + Waiting for finishing synchronisation... Warte auf Synchronisationsabschluss... @@ -1533,14 +1532,14 @@ Wilkommen zu einfacher und sicherer Bitmessage MsgDecode - + The message has an unknown encoding. Perhaps you should upgrade Bitmessage. Diese Bitmessage verwendtet eine unbekannte codierung. Womöglich sollten Sie Bitmessage upgraden. - + Unknown encoding Codierung unbekannt @@ -1849,77 +1848,77 @@ Die Zufallszahlen-Option ist standardmässig gewählt, jedoch haben deterministi Verbindungen insgesamt: - + Since startup: Seit Start: - + Processed 0 person-to-person messages. 0 Person-zu-Person-Nachrichten verarbeitet. - + Processed 0 public keys. 0 öffentliche Schlüssel verarbeitet. - + Processed 0 broadcasts. 0 Rundrufe verarbeitet. - + Inventory lookups per second: 0 Inventory lookups pro Sekunde: 0 - + Objects to be synced: Zu synchronisierende Objektanzahl: - + Stream # Datenstrom # Connections - Verbindungen + - + Since startup on %1 Seit Start der Anwendung am %1 - + Down: %1/s Total: %2 Herunter: %1/s Insg.: %2 - + Up: %1/s Total: %2 Hoch: %1/s Insg.: %2 - + Total Connections: %1 Verbindungen insgesamt: %1 - + Inventory lookups per second: %1 Inventory lookups pro Sekunde: %1 - + Up: 0 kB/s Hoch: 0 kB/s - + Down: 0 kB/s Herunter: 0 kB/s @@ -1929,30 +1928,75 @@ Die Zufallszahlen-Option ist standardmässig gewählt, jedoch haben deterministi Netzwerkstatus - + byte(s) ByteBytes - + Object(s) to be synced: %n %n Objekt zu synchronisieren.%n Objekte zu synchronisieren. - + Processed %n person-to-person message(s). %n Person-zu-Person-Nachricht bearbeitet.%n Person-zu-Person-Nachrichten bearbeitet. - + Processed %n broadcast message(s). %n Rundruf-Nachricht bearbeitet.%n Rundruf-Nachrichten bearbeitet. - + Processed %n public key(s). %n öffentlicher Schlüssel verarbeitet.%n öffentliche Schlüssel verarbeitet. + + + Peer + Peer + + + + IP address or hostname + IP-Adresse oder Hostname + + + + Rating + Bewertung + + + + PyBitmessage tracks the success rate of connection attempts to individual nodes. The rating ranges from -1 to 1 and affects the likelihood of selecting the node in the future + PyBitmessage verfolgt die Erfolgsrate von Verbindungsversuchen zu einzelnen Knoten. Die Bewertung reicht von -1 bis 1 und beeinflusst die Wahrscheinlichkeit, den Knoten zukünftig auszuwählen + + + + User agent + Benutzer-Agent + + + + Peer's self-reported software + Peer's selbstberichtete Software + + + + TLS + TLS + + + + Connection encryption + Verbindungsverschlüsselung + + + + List of streams negotiated between you and the peer + Liste der zwischen Ihnen und dem Peer ausgehandelten Datenströme + newChanDialog @@ -2014,12 +2058,12 @@ Die Zufallszahlen-Option ist standardmässig gewählt, jedoch haben deterministi Optional, for advanced usage - Optional, für fortgeschrittene + Optional, für Fortgeschrittene Chan address - Chan-adresse + Chan-Adresse @@ -2390,7 +2434,7 @@ Die Zufallszahlen-Option ist standardmässig gewählt, jedoch haben deterministi <html><head/><body><p>By default, if you send a message to someone and he is offline for more than two days, Bitmessage will send the message again after an additional two days. This will be continued with exponential backoff forever; messages will be resent after 5, 10, 20 days ect. until the receiver acknowledges them. Here you may change that behavior by having Bitmessage give up after a certain number of days or months.</p><p>Leave these input fields blank for the default behavior. </p></body></html> - <html><head/><body><p>Wenn der Empfänger eine Nachricht nicht bis zum Verfalldatum herunterlädt, zum Beisplel weil er für längere Zeit nicht mit dem Netz verbunden ist, wird die Nachricht erneut versendet. Dies passiert solange, bis eine Empfangsbestätigung erhalten wird. Hier können Sie dieses Verhalten ändern, indem Sie Bitmessage die Wiederversandversuche nach einer bestimmten Anzahl von Tagen oder Monaten aufgeben lassen.</p><p>Für die Standardeinstellung (ohne zeitliche Einschränkung) lassen Sie diese Eingabefelder leer.</p></body></html> + <html><head/><body><p>Wenn der Empfänger eine Nachricht nicht bis zum Verfallsdatum herunterlädt, zum Beispiel weil er für längere Zeit nicht mit dem Netz verbunden ist, wird die Nachricht erneut versendet. Dies passiert solange, bis eine Empfangsbestätigung erhalten wird. Hier können Sie dieses Verhalten ändern, indem Sie Bitmessage die Wiederversandversuche nach einer bestimmten Anzahl von Tagen oder Monaten aufgeben lassen.</p><p>Für die Standardeinstellung (ohne zeitliche Einschränkung) lassen Sie diese Eingabefelder leer.</p></body></html> From 4d0a40fd2aa50b2f0cd6ddfa590c6d7238fafc7a Mon Sep 17 00:00:00 2001 From: PyBitmessage Translations Date: Fri, 11 Aug 2017 09:46:06 +0200 Subject: [PATCH 185/407] Auto-updated language sk from transifex --- src/translations/bitmessage_sk.qm | Bin 88644 -> 89951 bytes src/translations/bitmessage_sk.ts | 329 +++++++++++++++++------------- 2 files changed, 187 insertions(+), 142 deletions(-) diff --git a/src/translations/bitmessage_sk.qm b/src/translations/bitmessage_sk.qm index 9a3e3670bf05e692e2084bef71abbc91bd73c4c9..525f4b38b7718e499d3278aec4920621a50ed13a 100644 GIT binary patch delta 2443 zcmZuydr(y875{yA-}f#uf+C=-7bc)G*#$A6i1HAtXocb!K!0~|LmZ>IuOqKb&1b`Ni)sL+a79WkGWae2YmHobIbS+!194v4_kn~CH%N~mYDpA`PVllfK8Ej zoMmnH_}iBC>lCn(JC=?6XHzL}Shh^{052T0yihm+_&C|J%!NmPbmj^pT(6>I^MdmZ7qqyWZgOU8d!BQ zcUueUinFX2Yl!N!?bcgX71;GJTf){(prh3`Sv&(~*W4GH|1 zc<`})VE+j59f_tKoguz|hJ^0O7f-A^Nb^}Oe!78L&{QH`S$YjPwO_m$rF6MJmssUK zV259Fv@1ZFDv74gkch5hlK590EGbEf>!O_-Ife%v>9)e^Oxn)Z_!ft&Gwn60>RrmY zJ5!oJum`N@rc}SWjB;Lup7lXjdf2Xmc}b~+vbHasl7)wd1kzbc(x z7XS_)mQG(K4zH|`{`g`QSh_BqYrh3HW4Cm^;(1`g@1+|FG~T~ol5WUNU^#it+?wU! z+2X|PoG+Z=IWJM}hn@O`A|T_p&i2I&_(g(q^Qo@_V9R$XUggNm;_c1>P-r#MJI#_GF@A9m9{)ZMw{_NowBMbHi@We~6ZE z)v$ioFtFsy@j02Pfc2RgU zhO^FEJVD_d1n5POfWwsV8%k@?mSfg{_tASR!S|t_(#hnh&|L?mlw;)uV<&@Tm~bNG zsgj3Ld~k@&qlA&ZqNs9vs8#l=K3Q*6ZJuGFCF(Dr@ zY9cFBBQ@fzFk}8SBWB!%>C{Xf8d;;oAzpMmrL&n%MvT$u5YcU+hPF|4#z>6%V%*@N zuL^p9;ANB$h-Dg7h=JXRcGAcL28OQ;H~nu94pWYuj6oUM!-U(8HdbLB42TYx+33y( zOpW|?gdU;C2P}+8gSC;tNA=YYRz{2iL;c@Z-996r2T63^21V8*-uh-OHc+j#zTS{x zghp-%q5i*X;MH{{7}m)z)F}s4U!X1EYxTCsYRIQ(9(fVTfuaIx0~xhJ(PV!l6qMZ) z$zPrrQ;gT|r delta 1329 zcmX9-c~I3=6#njSeZK`BJV6%WDI_S*cr3~y4<53*@)~0VQE>$oOu$e}41`FhTw$(; zI*>Av2Z7K5$s&7AI%WblX*=rE@7=qnJJ8J&H)C(gajv(F|t=kaU27> zLxmX~Pl4rB$UkXi>U|@u(OQ6q9}4eGECa+#^sbPuj>-`(J_-Su98ROeioo$6Vs&Lc zh|OEv6=wzZpBDE|4FP`i77sot1@SGVJz}tB@?r78^tXUefAKlr1QdshFH+rsAWd>F zWMTF^Df0-^!Q&PUmCPnvhV=2q3Sc2G)z7*MGCP6hO3}-w&X#(z7`2#l(qpLzXnZM~ zYPJAn8{`Gl9ZegmHAMjlsYa0E3PP2uZBT551MNTwur}-hU)9U)f(k+ zJPUu=ubgURnykF3bVT}sxLs5_hCXBIm*@#Yut{>N+B&8rZm6R&n5(89de>l`6;rOR z&SO#b0kyC%3ivhXKO{a!)rv(AKqe)q2k*TGuC%Hx_J7!9|E`|S9tOVhP|s@2-nwM< z`%X65LKn?d%-p07TCSK?cbn!?#a6~Gi5^h`Xs6=8#rvw}_?9I$1!;3fjxo_q+KSCd zAY!JrqpX^tzgBahn-$!s9qdX0@wun3jn86yVleU{ND+IA*>)~08K?~Igi)BbJnb`mmJy&0U z#|8vm*Ei)5;J8WO(Ov@#UDWp`GU?LtX}fCYY^_w(g4HQ~`kl>PY2!)8}DhoUho$QLm586%4} zfJ9^)qpq1jtUnuVWA=i$U!m=~H4sK)RXV$t4aPm)Y!l1NjZNm;K+zgwbH^}<*^PR- zM9a1pZ1^%J(6z5pPk-t%GRV6jv#xR)@zS-F;-aGb73&L%i%RPPbmCR_Tp{P26ExyZ foC7+!N}QXGq(rET8BK0FqrFHTao+bP@1_3>#bbn< diff --git a/src/translations/bitmessage_sk.ts b/src/translations/bitmessage_sk.ts index 98c7cb56..d58d4f46 100644 --- a/src/translations/bitmessage_sk.ts +++ b/src/translations/bitmessage_sk.ts @@ -58,12 +58,12 @@ EmailGatewayRegistrationDialog - + Registration failed: Registrácia zlyhala: - + The requested email address is not available, please try a new one. Fill out the new desired email address (including @mailchuck.com) below: Požadovaná e-mailová adresa nie je k dispozícii, skúste znova. Vyplňte novú požadovanú e-mailovú adresu (vrátane @mailchuck.com) nižšie: @@ -321,7 +321,7 @@ Vyplňte požadovanú e-mailovú adresu (vrátane @mailchuck.com) nižšie:Potvrdenie prijatia správy %1 - + Broadcast queued. Rozoslanie vo fronte. @@ -351,7 +351,7 @@ Vyplňte požadovanú e-mailovú adresu (vrátane @mailchuck.com) nižšie:Neznámy stav: %1 %2 - + Not Connected Nepripojený @@ -514,22 +514,22 @@ Tento súbor je dôležité zálohovať. Chcete tento súbor teraz otvoriť? (Ne - + Connection lost Spojenie bolo stratené - + Connected Spojený - + Message trashed Správa odstránenia - + The TTL, or Time-To-Live is the length of time that the network will hold the message. The recipient must get it during this time. If your Bitmessage client does not hear an acknowledgement, it will resend the message automatically. The longer the Time-To-Live, the @@ -537,17 +537,17 @@ Tento súbor je dôležité zálohovať. Chcete tento súbor teraz otvoriť? (Ne TTL (doba životnosti) je čas, počas ktorého bude sieť udržiavať správu. Príjemca musí správu prijať počas tejto životnosti. Keď odosielateľov Bitmessage nedostane po vypršaní životnosti potvrdenie o prijatí, automaticky správu odošle znova. Čím vyššia doba životnosti, tým viac práce musí počítač odosielateľa vykonat na odoslanie správy. Zvyčajne je vhodná doba životnosti okolo štyroch-piatich dní. - + Message too long Správa je príliš dlhá - + The message that you are trying to send is too long by %1 bytes. (The maximum is 261644 bytes). Please cut it down before sending. Správa, ktorú skúšate poslať, má %1 bajtov naviac. (Maximum je 261 644 bajtov). Prosím pred odoslaním skrátiť. - + Error: Your account wasn't registered at an email gateway. Sending registration now as %1, please wait for the registration to be processed before retrying sending. Chyba: Váš účet nebol registrovaný na e-mailovej bráne. Skúšam registrovať ako %1, prosím počkajte na spracovanie registrácie pred opakovaným odoslaním správy. @@ -592,67 +592,67 @@ Tento súbor je dôležité zálohovať. Chcete tento súbor teraz otvoriť? (Ne - + Error: You must specify a From address. If you don't have one, go to the 'Your Identities' tab. Chyba: musíte zadať adresu "Od". Ak žiadnu nemáte, prejdite na kartu "Vaše identity". - + Address version number Číslo verzie adresy - + Concerning the address %1, Bitmessage cannot understand address version numbers of %2. Perhaps upgrade Bitmessage to the latest version. Čo sa týka adresy %1, Bitmessage nepozná číslo verzie adresy %2. Možno by ste mali upgradenúť Bitmessage na najnovšiu verziu. - + Stream number Číslo prúdu - + Concerning the address %1, Bitmessage cannot handle stream numbers of %2. Perhaps upgrade Bitmessage to the latest version. Čo sa týka adresy %1, Bitmessage nespracováva číslo prúdu %2. Možno by ste mali upgradenúť Bitmessage na najnovšiu verziu. - + Warning: You are currently not connected. Bitmessage will do the work necessary to send the message but it won't send until you connect. Upozornenie: momentálne nie ste pripojení. Bitmessage vykoná prácu potrebnú na odoslanie správy, ale odoslať ju môže, až keď budete pripojení. - + Message queued. Správa vo fronte. - + Your 'To' field is empty. Pole "Komu" je prázdne. - + Right click one or more entries in your address book and select 'Send message to this address'. Vybertie jednu alebo viacero položiek v adresári, pravým tlačidlom myši zvoľte "Odoslať správu na túto adresu". - + Fetched address from namecoin identity. Prebratá adresa z namecoin-ovej identity. - + New Message Nová správa - + From Od - + Sending email gateway registration request Odosielam požiadavku o registráciu na e-mailovej bráne @@ -667,142 +667,142 @@ Tento súbor je dôležité zálohovať. Chcete tento súbor teraz otvoriť? (Ne Zadaná adresa bola neplatná a bude ignorovaná. - + Error: You cannot add the same address to your address book twice. Try renaming the existing one if you want. Chyba: tú istú adresu nemožno pridať do adresára dvakrát. Ak chcete, môžete skúsiť premenovať existujúcu menovku. - + Error: You cannot add the same address to your subscriptions twice. Perhaps rename the existing one if you want. Chyba: nemožno pridať rovnakú adresu k odberu dvakrát. Keď chcete, môžete premenovať existujúci záznam. - + Restart Reštart - + You must restart Bitmessage for the port number change to take effect. Aby sa zmena čísla portu prejavila, musíte reštartovať Bitmessage. - + Bitmessage will use your proxy from now on but you may want to manually restart Bitmessage now to close existing connections (if any). Bitmessage bude odteraz používať proxy, ale ak chcete ukončiť existujúce spojenia, musíte Bitmessage manuálne reštartovať. - + Number needed Číslo potrebné - + Your maximum download and upload rate must be numbers. Ignoring what you typed. Maxímálna rýchlosť príjmu a odoslania musí byť uvedená v číslach. Ignorujem zadané údaje. - + Will not resend ever Nikdy opätovne neodosielať - + Note that the time limit you entered is less than the amount of time Bitmessage waits for the first resend attempt therefore your messages will never be resent. Upozornenie: časový limit, ktorý ste zadali, je menší ako čas, ktorý Bitmessage čaká na prvý pokus o opätovné zaslanie, a preto vaše správy nebudú nikdy opätovne odoslané. - + Sending email gateway unregistration request Odosielam žiadosť o odhlásenie z e-mailovej brány - + Sending email gateway status request Odosielam požiadavku o stave e-mailovej brány - + Passphrase mismatch Nezhoda hesla - + The passphrase you entered twice doesn't match. Try again. Zadané heslá sa rôznia. Skúste znova. - + Choose a passphrase Vyberte heslo - + You really do need a passphrase. Heslo je skutočne potrebné. - + Address is gone Adresa zmizla - + Bitmessage cannot find your address %1. Perhaps you removed it? Bitmessage nemôže nájsť vašu adresu %1. Možno ste ju odstránili? - + Address disabled Adresa deaktivovaná - + Error: The address from which you are trying to send is disabled. You'll have to enable it on the 'Your Identities' tab before using it. Chyba: adresa, z ktorej sa pokúšate odoslať, je neaktívna. Pred použitím ju musíte aktivovať v karte "Vaše identity". - + Entry added to the Address Book. Edit the label to your liking. Záznam pridaný do adresára. Upravte označenie podľa vašich predstáv. - + Entry added to the blacklist. Edit the label to your liking. Záznam pridaný na zoznam zakázaných. Upravte označenie podľa vašich predstáv. - + Error: You cannot add the same address to your blacklist twice. Try renaming the existing one if you want. Chyba: tú istú adresu nemožno pridať na zoznam zakázaných dvakrát. Ak chcete, môžete skúsiť premenovať existujúce označenie. - + Moved items to trash. Položky presunuté do koša. - + Undeleted item. Položka obnovená. - + Save As... Uložiť ako... - + Write error. Chyba pri zapisovaní. - + No addresses selected. Nevybraná žiadna adresa. - + If you delete the subscription, messages that you already received will become inaccessible. Maybe you can consider disabling the subscription instead. Disabled subscriptions will not receive new messages, but you can still view messages you already received. Are you sure you want to delete the subscription? @@ -811,7 +811,7 @@ Are you sure you want to delete the subscription? Ste si istý, že chcete odber odstrániť? - + If you delete the channel, messages that you already received will become inaccessible. Maybe you can consider disabling the channel instead. Disabled channels will not receive new messages, but you can still view messages you already received. Are you sure you want to delete the channel? @@ -820,92 +820,92 @@ Are you sure you want to delete the channel? Ste si istý, že chcete kanál odstrániť? - + Do you really want to remove this avatar? Naozaj chcete odstrániť tento avatar? - + You have already set an avatar for this address. Do you really want to overwrite it? Pre túto adresu ste už ste nastavili avatar. Naozaj ho chcete ho zmeniť? - + Start-on-login not yet supported on your OS. Spustenie pri prihlásení zatiaľ pre váš operačný systém nie je podporované. - + Minimize-to-tray not yet supported on your OS. Minimalizovanie do panelu úloh zatiaľ pre váš operačný systém nie je podporované. - + Tray notifications not yet supported on your OS. Oblasť oznámení zatiaľ pre váš operačný systém nie je podporovaná. - + Testing... Testujem... - + This is a chan address. You cannot use it as a pseudo-mailing list. Toto je adresa kanálu. Nie je možné ju používať ako pseudo poštový zoznam. - + The address should start with ''BM-'' Adresa by mala začínať ''BM-'' - + The address is not typed or copied correctly (the checksum failed). Nesprávne zadaná alebo skopírovaná adresa (kontrolný súčet zlyhal). - + The version number of this address is higher than this software can support. Please upgrade Bitmessage. Číslo verzie tejto adresy je vyššie ako tento softvér podporuje. Prosím inovujte Bitmessage. - + The address contains invalid characters. Adresa obsahuje neplatné znaky. - + Some data encoded in the address is too short. Niektoré dáta zakódované v adrese sú príliš krátke. - + Some data encoded in the address is too long. Niektoré dáta zakódované v adrese sú príliš dlhé. - + Some data encoded in the address is malformed. Niektoré dáta zakódované v adrese sú poškodené. - + Enter an address above. Zadajte adresu vyššie. - + Address is an old type. We cannot display its past broadcasts. Starý typ adresy. Nie je možné zobraziť jej predchádzajúce hromadné správy. - + There are no recent broadcasts from this address to display. Neboli nájdené žiadne nedávne hromadé správy z tejto adresy. - + You are using TCP port %1. (This can be changed in the settings). Používate port TCP %1. (Možno zmeniť v nastaveniach). @@ -1115,47 +1115,47 @@ Ste si istý, že chcete kanál odstrániť? Pridať nový záznam - + Display the %1 recent broadcast(s) from this address. Zobraziť posledných %1 hromadných správ z tejto adresy. - + New version of PyBitmessage is available: %1. Download it from https://github.com/Bitmessage/PyBitmessage/releases/latest K dispozícii je nová verzia PyBitmessage: %1. Môžete ju stiahnuť na https://github.com/Bitmessage/PyBitmessage/releases/latest - + Waiting for PoW to finish... %1% Čakám na ukončenie práce... %1% - + Shutting down Pybitmessage... %1% Ukončujem PyBitmessage... %1% - + Waiting for objects to be sent... %1% Čakám na odoslanie objektov... %1% - + Saving settings... %1% Ukladám nastavenia... %1% - + Shutting down core... %1% Ukončujem jadro... %1% - + Stopping notifications... %1% Zastavujem oznámenia... %1% - + Shutdown imminent... %1% Posledná fáza ukončenia... %1% @@ -1170,7 +1170,7 @@ Ste si istý, že chcete kanál odstrániť? %n deň%n dni%n dní - + Shutting down PyBitmessage... %1% Ukončujem PyBitmessage... %1% @@ -1220,61 +1220,61 @@ Ste si istý, že chcete kanál odstrániť? Upozornenie: Váš disk alebo priestor na ukladanie dát je plný. Bitmessage bude teraz ukončený. - + Error! Could not find sender address (your address) in the keys.dat file. Chyba! Nemožno nájsť adresu odosielateľa (vašu adresu) v súbore keys.dat. - + Doing work necessary to send broadcast... Vykonávam prácu potrebnú na rozoslanie... - + Broadcast sent on %1 Rozoslané %1 - + Encryption key was requested earlier. Šifrovací klúč bol vyžiadaný. - + Sending a request for the recipient's encryption key. Odosielam požiadavku na kľúč príjemcu. - + Looking up the receiver's public key Hľadám príjemcov verejný kľúč - + Problem: Destination is a mobile device who requests that the destination be included in the message but this is disallowed in your settings. %1 Problém: adresa príjemcu je na mobilnom zariadení a požaduje, aby správy obsahovali nezašifrovanú adresu príjemcu. Vaše nastavenia však túto možnost nemajú povolenú. %1 - + Doing work necessary to send message. There is no required difficulty for version 2 addresses like this. Vykonávam prácu potrebnú na odoslanie správy. Adresy verzie dva, ako táto, nepožadujú obtiažnosť. - + Doing work necessary to send message. Receiver's required difficulty: %1 and %2 Vykonávam prácu potrebnú na odoslanie správy. Priímcova požadovaná obtiažnosť: %1 a %2 - + Problem: The work demanded by the recipient (%1 and %2) is more difficult than you are willing to do. %3 Problém: Práca požadovná príjemcom (%1 a %2) je obtiažnejšia, ako máte povolené. %3 - + Problem: You are trying to send a message to yourself or a chan but your encryption key could not be found in the keys.dat file. Could not encrypt message. %1 Problém: skúšate odslať správu sami sebe, ale nemôžem nájsť šifrovací kľúč v súbore keys.dat. Nemožno správu zašifrovať: %1 @@ -1284,7 +1284,7 @@ Priímcova požadovaná obtiažnosť: %1 a %2 Vykonávam prácu potrebnú na odoslanie... - + Message sent. Waiting for acknowledgement. Sent on %1 Správa odoslaná. Čakanie na potvrdenie. Odoslaná %1 @@ -1294,22 +1294,22 @@ Priímcova požadovaná obtiažnosť: %1 a %2 Vykonávam prácu potrebnú na vyžiadanie šifrovacieho kľúča. - + Broadcasting the public key request. This program will auto-retry if they are offline. Rozosielam požiadavku na verejný kľúč. Ak nebude príjemca spojený zo sieťou, budem skúšať znova. - + Sending public key request. Waiting for reply. Requested at %1 Odosielam požiadavku na verejný kľúč. Čakám na odpoveď. Vyžiadaný %1 - + UPnP port mapping established on port %1 Mapovanie portov UPnP vytvorené na porte %1 - + UPnP port mapping removed Mapovanie portov UPnP zrušené @@ -1319,7 +1319,7 @@ Priímcova požadovaná obtiažnosť: %1 a %2 Označiť všetky správy ako prečítané - + Are you sure you would like to mark all messages read? Ste si istý, že chcete označiť všetky správy ako prečítané? @@ -1329,37 +1329,37 @@ Priímcova požadovaná obtiažnosť: %1 a %2 Vykonávam prácu potrebnú na rozoslanie. - + Proof of work pending Vykonávaná práca - + %n object(s) pending proof of work %n objekt čaká na vykonanie práce%n objekty čakajú na vykonanie práce%n objektov čaká na vykonanie práce - + %n object(s) waiting to be distributed %n objekt čaká na rozoslanie%n objekty čakajú na rozoslanie%n objektov čaká na rozoslanie - + Wait until these tasks finish? Počkať, kým tieto úlohy skončia? - + Problem communicating with proxy: %1. Please check your network settings. Problém komunikácie s proxy: %1. Prosím skontrolujte nastavenia siete. - + SOCKS5 Authentication problem: %1. Please check your SOCKS5 settings. Problém autentikácie SOCKS5: %1. Prosím skontrolujte nastavenia SOCKS5. - + The time on your computer, %1, may be wrong. Please verify your settings. Čas na vašom počítači, %1, možno nie je správny. Prosím, skontrolujete nastavenia. @@ -1429,77 +1429,77 @@ Vitajte v jednoduchom a bezpečnom Bitmessage nie je odporúčaná pre kanály - + Problems connecting? Try enabling UPnP in the Network Settings Problémy so spojením? Skúste zapnúť UPnP v Nastaveniach siete - + Error: Bitmessage addresses start with BM- Please check the recipient address %1 Chyba: Bitmessage adresy začínajú s BM- Prosím skontrolujte adresu príjemcu %1 - + Error: The recipient address %1 is not typed or copied correctly. Please check it. Chyba: adresa príjemcu %1 nie je na správne napísaná alebo skopírovaná. Prosím skontrolujte ju. - + Error: The recipient address %1 contains invalid characters. Please check it. Chyba: adresa príjemcu %1 obsahuje neplatné znaky. Prosím skontrolujte ju. - + Error: The version of the recipient address %1 is too high. Either you need to upgrade your Bitmessage software or your acquaintance is being clever. Chyba: verzia adresy príjemcu %1 je príliš veľká. Buď musíte aktualizovať program Bitmessage alebo váš známy s vami žartuje. - + Error: Some data encoded in the recipient address %1 is too short. There might be something wrong with the software of your acquaintance. Chyba: niektoré údaje zakódované v adrese príjemcu %1 sú príliš krátke. Softér vášho známeho možno nefunguje správne. - + Error: Some data encoded in the recipient address %1 is too long. There might be something wrong with the software of your acquaintance. Chyba: niektoré údaje zakódované v adrese príjemcu %1 sú príliš dlhé. Softvér vášho známeho možno nefunguje správne. - + Error: Some data encoded in the recipient address %1 is malformed. There might be something wrong with the software of your acquaintance. Chyba: niektoré údaje zakódované v adrese príjemcu %1 sú poškodené. Softvér vášho známeho možno nefunguje správne. - + Error: Something is wrong with the recipient address %1. Chyba: niečo s adresou príjemcu %1 je nie je v poriadku. - + Synchronisation pending Prebieha synchronizácia - + Bitmessage hasn't synchronised with the network, %n object(s) to be downloaded. If you quit now, it may cause delivery delays. Wait until the synchronisation finishes? Bitmessage sa nezosynchronizoval so sieťou, treba prevzať ešte %n objekt. Keď program ukončíte teraz, môže dôjsť k spozdeniu doručenia. Počkať, kým sa synchronizácia ukončí?Bitmessage sa nezosynchronizoval so sieťou, treba prevzať ešte %n objekty. Keď program ukončíte teraz, môže dôjsť k spozdeniu doručenia. Počkať, kým sa synchronizácia ukončí?Bitmessage sa nezosynchronizoval so sieťou, treba prevzať ešte %n objektov. Keď program ukončíte teraz, môže dôjsť k spozdeniu doručenia. Počkať, kým sa synchronizácia ukončí? - + Not connected Nepripojený - + Bitmessage isn't connected to the network. If you quit now, it may cause delivery delays. Wait until connected and the synchronisation finishes? Bitmessage nie je pripojený do siete. Keď program ukončíte teraz, môže dôjsť k spozdeniu doručenia. Počkať, kým dôjde k spojeniu a prebehne synchronizácia? - + Waiting for network connection... Čakám na pripojenie do siete... - + Waiting for finishing synchronisation... Čakám na ukončenie synchronizácie... @@ -1530,14 +1530,14 @@ Vitajte v jednoduchom a bezpečnom Bitmessage MsgDecode - + The message has an unknown encoding. Perhaps you should upgrade Bitmessage. Správa má neznáme kódovanie. Možno by ste mali inovovať Bitmessage. - + Unknown encoding Neznáme kódovanie @@ -1846,77 +1846,77 @@ Predvoľba je pomocou generátora náhodných čísiel, ale deterministické adr Spojení spolu: - + Since startup: Od spustenia: - + Processed 0 person-to-person messages. Spracovaných 0 bežných správ. - + Processed 0 public keys. Spracovaných 0 verejných kľúčov. - + Processed 0 broadcasts. Spracovaných 0 hromadných správ. - + Inventory lookups per second: 0 Vyhľadaní v inventári za sekundu: 0 - + Objects to be synced: Zostáva synchronizovať objektov: - + Stream # Prúd # Connections - Spojenia + - + Since startup on %1 Od spustenia %1 - + Down: %1/s Total: %2 Prijatých: %1/s Spolu: %2 - + Up: %1/s Total: %2 Odoslaných: %1/s Spolu: %2 - + Total Connections: %1 Spojení spolu: %1 - + Inventory lookups per second: %1 Vyhľadaní v inventári za sekundu: %1 - + Up: 0 kB/s Odoslaných: 0 kB/s - + Down: 0 kB/s Prijatých: 0 kB/s @@ -1926,30 +1926,75 @@ Predvoľba je pomocou generátora náhodných čísiel, ale deterministické adr Stav siete - + byte(s) bajtbajtybajtov - + Object(s) to be synced: %n Zostáva synchronizovať %n objektZostáva synchronizovať %n objektyZostáva synchronizovať %n objektov - + Processed %n person-to-person message(s). Spracovaná %n bežná správa.Spracované %n bežné správy.Spracovaných %n bežných správ. - + Processed %n broadcast message(s). Spracovaná %n hromadná správa.Spracované %n hromadné správy.Spracovaných %n hromadných správ. - + Processed %n public key(s). Spracovaný %n verejný kľúč.Spracované %n verejné kľúče.Spracovaných %n verejných kľúčov. + + + Peer + partnerský uzol + + + + IP address or hostname + IP adresa alebo názov hostiteľa + + + + Rating + Hodnotenie + + + + PyBitmessage tracks the success rate of connection attempts to individual nodes. The rating ranges from -1 to 1 and affects the likelihood of selecting the node in the future + PyBitmessage sleduje úspešnosť pokusov o pripojenie k jednotlivým uzlom. Hodnotenie sa pohybuje od -1 do 1 a ovplyvňuje pravdepodobnosť výberu uzla v budúcnosti + + + + User agent + Užívateľský agent + + + + Peer's self-reported software + Software hlásený partnerským uzlom + + + + TLS + TLS + + + + Connection encryption + Šifrovanie pripojenia + + + + List of streams negotiated between you and the peer + Zoznam prúdov dohodnutých medzi vami a partnerom + newChanDialog From 3c50615998cbb641742dc55c9573bf0de9313601 Mon Sep 17 00:00:00 2001 From: PyBitmessage Translations Date: Sun, 13 Aug 2017 16:54:52 +0200 Subject: [PATCH 186/407] Auto-updated language zh_cn from transifex --- src/translations/bitmessage_zh_cn.qm | Bin 54427 -> 57332 bytes src/translations/bitmessage_zh_cn.ts | 697 ++++++++++++++++----------- 2 files changed, 421 insertions(+), 276 deletions(-) diff --git a/src/translations/bitmessage_zh_cn.qm b/src/translations/bitmessage_zh_cn.qm index 14625aef1df9c67a46a6983006d20f671c2b9baa..77b56321fd51c739be799b37851bf168ca4230b0 100644 GIT binary patch delta 5887 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 delta 3358 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 KtiN3KH~t?N0`l_! diff --git a/src/translations/bitmessage_zh_cn.ts b/src/translations/bitmessage_zh_cn.ts index 323bf123..263477d3 100644 --- a/src/translations/bitmessage_zh_cn.ts +++ b/src/translations/bitmessage_zh_cn.ts @@ -58,12 +58,12 @@ EmailGatewayRegistrationDialog - + Registration failed: 注册失败: - + The requested email address is not available, please try a new one. Fill out the new desired email address (including @mailchuck.com) below: 要求的电子邮件地址不详,请尝试一个新的。填写新的所需电子邮件地址(包括 @mailchuck.com)如下: @@ -82,7 +82,7 @@ Please type the desired email address (including @mailchuck.com) below: Mailchuck - + # You can use this to configure your email gateway account # Uncomment the setting you want to use # Here are the options: @@ -166,122 +166,122 @@ Please type the desired email address (including @mailchuck.com) below: MainWindow - + Reply to sender 回复发件人 - + Reply to channel 回复通道 - + Add sender to your Address Book 将发送者添加到您的通讯簿 - + Add sender to your Blacklist 将发件人添加到您的黑名单 - + Move to Trash 移入回收站 - + Undelete 取消删除 - + View HTML code as formatted text 作为HTML查看 - + Save message as... 将消息保存为... - + Mark Unread 标记为未读 - + New 新建 - + Enable 启用 - + Disable 禁用 - + Set avatar... 设置头像... - + Copy address to clipboard 将地址复制到剪贴板 - + Special address behavior... 特别的地址行为... - + Email gateway 电子邮件网关 - + Delete 删除 - + Send message to this address 发送消息到这个地址 - + Subscribe to this address 订阅到这个地址 - + Add New Address 创建新地址 - + Copy destination address to clipboard 复制目标地址到剪贴板 - + Force send 强制发送 - + One of your addresses, %1, is an old version 1 address. Version 1 addresses are no longer supported. May we delete it now? 您的地址中的一个, %1,是一个过时的版本1地址. 版本1地址已经不再受到支持了. 我们可以将它删除掉么? - + Waiting for their encryption key. Will request it again soon. 正在等待他们的加密密钥,我们会在稍后再次请求。 @@ -291,17 +291,17 @@ Please type the desired email address (including @mailchuck.com) below: - + Queued. 已经添加到队列。 - + Message sent. Waiting for acknowledgement. Sent at %1 消息已经发送. 正在等待回执. 发送于 %1 - + Message sent. Sent at %1 消息已经发送. 发送于 %1 @@ -311,47 +311,47 @@ Please type the desired email address (including @mailchuck.com) below: - + Acknowledgement of the message received %1 消息的回执已经收到于 %1 - + Broadcast queued. 广播已经添加到队列中。 - + Broadcast on %1 已经广播于 %1 - + Problem: The work demanded by the recipient is more difficult than you are willing to do. %1 错误: 收件人要求的做工量大于我们的最大接受做工量。 %1 - + Problem: The recipient's encryption key is no good. Could not encrypt message. %1 错误: 收件人的加密密钥是无效的。不能加密消息。 %1 - + Forced difficulty override. Send should start soon. 已经忽略最大做工量限制。发送很快就会开始。 - + Unknown status: %1 %2 未知状态: %1 %2 - + Not Connected 未连接 - + Show Bitmessage 显示比特信 @@ -361,12 +361,12 @@ Please type the desired email address (including @mailchuck.com) below: 发送 - + Subscribe 订阅 - + Channel 频道 @@ -376,66 +376,66 @@ Please type the desired email address (including @mailchuck.com) below: 退出 - + You may manage your keys by editing the keys.dat file stored in the same directory as this program. It is important that you back up this file. 您可以通过编辑和程序储存在同一个目录的 keys.dat 来编辑密钥。备份这个文件十分重要。 - + You may manage your keys by editing the keys.dat file stored in %1 It is important that you back up this file. 您可以通过编辑储存在 %1 的 keys.dat 来编辑密钥。备份这个文件十分重要。 - + Open keys.dat? 打开 keys.dat ? - + You may manage your keys by editing the keys.dat file stored in the same directory as this program. It is important that you back up this file. Would you like to open the file now? (Be sure to close Bitmessage before making any changes.) 您可以通过编辑和程序储存在同一个目录的 keys.dat 来编辑密钥。备份这个文件十分重要。您现在想打开这个文件么?(请在进行任何修改前关闭比特信) - + You may manage your keys by editing the keys.dat file stored in %1 It is important that you back up this file. Would you like to open the file now? (Be sure to close Bitmessage before making any changes.) 您可以通过编辑储存在 %1 的 keys.dat 来编辑密钥。备份这个文件十分重要。您现在想打开这个文件么?(请在进行任何修改前关闭比特信) - + Delete trash? 清空回收站? - + Are you sure you want to delete all trashed messages? 您确定要删除全部被回收的消息么? - + bad passphrase 错误的密钥 - + You must type your passphrase. If you don't have one then this is not the form for you. 您必须输入您的密钥。如果您没有的话,这个表单不适用于您。 - + Bad address version number 地址的版本号无效 - + Your address version number must be a number: either 3 or 4. 您的地址的版本号必须是一个数字: 3 或 4. - + Your address version number must be either 3 or 4. 您的地址的版本号必须是 3 或 4. @@ -505,22 +505,22 @@ It is important that you back up this file. Would you like to open the file now? - + Connection lost 连接已丢失 - + Connected 已经连接 - + Message trashed 消息已经移入回收站 - + The TTL, or Time-To-Live is the length of time that the network will hold the message. The recipient must get it during this time. If your Bitmessage client does not hear an acknowledgement, it will resend the message automatically. The longer the Time-To-Live, the @@ -529,17 +529,17 @@ It is important that you back up this file. Would you like to open the file now? 收件人必须在此期间得到它. 如果您的Bitmessage客户沒有听到确认, 它会自动重新发送信息. Time-To-Live的时间越长, 您的电脑必须要做更多工作來发送信息. 四天或五天的 Time-To-Time, 经常是合适的. - + Message too long 信息太长 - + The message that you are trying to send is too long by %1 bytes. (The maximum is 261644 bytes). Please cut it down before sending. 你正在尝试发送的信息已超过%1个字节太长, (最大为261644个字节). 发送前请剪下来。 - + Error: Your account wasn't registered at an email gateway. Sending registration now as %1, please wait for the registration to be processed before retrying sending. 错误: 您的帐户没有在电子邮件网关注册。现在发送注册为%1​​, 注册正在处理请稍候重试发送. @@ -584,217 +584,217 @@ It is important that you back up this file. Would you like to open the file now? - + Error: You must specify a From address. If you don't have one, go to the 'Your Identities' tab. 错误: 您必须指出一个表单地址, 如果您没有,请到“您的身份”标签页。 - + Address version number 地址版本号 - + Concerning the address %1, Bitmessage cannot understand address version numbers of %2. Perhaps upgrade Bitmessage to the latest version. 地址 %1 的地址版本号 %2 无法被比特信理解。也许你应该升级你的比特信到最新版本。 - + Stream number 节点流序号 - + Concerning the address %1, Bitmessage cannot handle stream numbers of %2. Perhaps upgrade Bitmessage to the latest version. 地址 %1 的节点流序号 %2 无法被比特信理解。也许你应该升级你的比特信到最新版本。 - + Warning: You are currently not connected. Bitmessage will do the work necessary to send the message but it won't send until you connect. 警告: 您尚未连接。 比特信将做足够的功来发送消息,但是消息不会被发出直到您连接。 - + Message queued. 信息排队。 - + Your 'To' field is empty. “收件人"是空的。 - + Right click one or more entries in your address book and select 'Send message to this address'. 在您的地址本的一个条目上右击,之后选择”发送消息到这个地址“。 - + Fetched address from namecoin identity. 已经自namecoin接收了地址。 - + New Message 新消息 - + From 来自 - + Sending email gateway registration request 发送电​​子邮件网关注册请求 - + Address is valid. 地址有效。 - + The address you entered was invalid. Ignoring it. 您输入的地址是无效的,将被忽略。 - + Error: You cannot add the same address to your address book twice. Try renaming the existing one if you want. 错误:您无法将一个地址添加到您的地址本两次,请尝试重命名已经存在的那个。 - + Error: You cannot add the same address to your subscriptions twice. Perhaps rename the existing one if you want. 错误: 您不能在同一地址添加到您的订阅两次. 也许您可重命名现有之一. - + Restart 重启 - + You must restart Bitmessage for the port number change to take effect. 您必须重启以便使比特信对于使用的端口的改变生效。 - + Bitmessage will use your proxy from now on but you may want to manually restart Bitmessage now to close existing connections (if any). 比特信将会从现在开始使用代理,但是您可能想手动重启比特信以便使之前的连接关闭(如果有的话)。 - + Number needed 需求数字 - + Your maximum download and upload rate must be numbers. Ignoring what you typed. 您最大的下载和上传速率必须是数字. 忽略您键入的内容. - + Will not resend ever 不尝试再次发送 - + Note that the time limit you entered is less than the amount of time Bitmessage waits for the first resend attempt therefore your messages will never be resent. 请注意,您所输入的时间限制小于比特信的最小重试时间,因此您将永远不会重发消息。 - + Sending email gateway unregistration request 发送电​​子邮件网关注销请求 - + Sending email gateway status request 发送电​​子邮件网关状态请求 - + Passphrase mismatch 密钥不匹配 - + The passphrase you entered twice doesn't match. Try again. 您两次输入的密码并不匹配,请再试一次。 - + Choose a passphrase 选择一个密钥 - + You really do need a passphrase. 您真的需要一个密码。 - + Address is gone 已经失去了地址 - + Bitmessage cannot find your address %1. Perhaps you removed it? 比特信无法找到你的地址 %1。 也许你已经把它删掉了? - + Address disabled 地址已经禁用 - + Error: The address from which you are trying to send is disabled. You'll have to enable it on the 'Your Identities' tab before using it. 错误: 您想以一个您已经禁用的地址发出消息。在使用之前您需要在“您的身份”处再次启用。 - + Entry added to the Address Book. Edit the label to your liking. 条目已经添加到地址本。您可以去修改您的标签。 - + Entry added to the blacklist. Edit the label to your liking. 条目添加到黑名单. 根据自己的喜好编辑标签. - + Error: You cannot add the same address to your blacklist twice. Try renaming the existing one if you want. 错误: 您不能在同一地址添加到您的黑名单两次. 也许您可重命名现有之一. - + Moved items to trash. 已经移动项目到回收站。 - + Undeleted item. 未删除的项目。 - + Save As... 另存为... - + Write error. 写入失败。 - + No addresses selected. 没有选择地址。 - + If you delete the subscription, messages that you already received will become inaccessible. Maybe you can consider disabling the subscription instead. Disabled subscriptions will not receive new messages, but you can still view messages you already received. Are you sure you want to delete the subscription? @@ -803,7 +803,7 @@ Are you sure you want to delete the subscription? 你确定要删除订阅? - + If you delete the channel, messages that you already received will become inaccessible. Maybe you can consider disabling the channel instead. Disabled channels will not receive new messages, but you can still view messages you already received. Are you sure you want to delete the channel? @@ -812,92 +812,92 @@ Are you sure you want to delete the channel? 你确定要删除频道? - + Do you really want to remove this avatar? 您真的想移除这个头像么? - + You have already set an avatar for this address. Do you really want to overwrite it? 您已经为这个地址设置了头像了。您真的想移除么? - + Start-on-login not yet supported on your OS. 登录时启动尚未支持您在使用的操作系统。 - + Minimize-to-tray not yet supported on your OS. 最小化到托盘尚未支持您的操作系统。 - + Tray notifications not yet supported on your OS. 托盘提醒尚未支持您所使用的操作系统。 - + Testing... 正在测试... - + This is a chan address. You cannot use it as a pseudo-mailing list. 这是一个频道地址,您无法把它作为伪邮件列表。 - + The address should start with ''BM-'' 地址应该以"BM-"开始 - + The address is not typed or copied correctly (the checksum failed). 地址没有被正确的键入或复制(校验码校验失败)。 - + The version number of this address is higher than this software can support. Please upgrade Bitmessage. 这个地址的版本号大于此软件的最大支持。 请升级比特信。 - + The address contains invalid characters. 这个地址中包含无效字符。 - + Some data encoded in the address is too short. 在这个地址中编码的部分信息过少。 - + Some data encoded in the address is too long. 在这个地址中编码的部分信息过长。 - + Some data encoded in the address is malformed. 在地址编码的某些数据格式不正确. - + Enter an address above. 请在上方键入地址。 - + Address is an old type. We cannot display its past broadcasts. 地址没有近期的广播。我们无法显示之间的广播。 - + There are no recent broadcasts from this address to display. 没有可以显示的近期广播。 - + You are using TCP port %1. (This can be changed in the settings). 您正在使用TCP端口 %1 。(可以在设置中修改)。 @@ -1097,57 +1097,57 @@ Are you sure you want to delete the channel? 缩放级别%1% - + Error: You cannot add the same address to your list twice. Perhaps rename the existing one if you want. 错误: 您不能在同一地址添加到列表中两次. 也许您可重命名现有之一. - + Add new entry 添加新条目 - + Display the %1 recent broadcast(s) from this address. 显示从这个地址%1的最近广播 - + New version of PyBitmessage is available: %1. Download it from https://github.com/Bitmessage/PyBitmessage/releases/latest PyBitmessage的新版本可用: %1. 从https://github.com/Bitmessage/PyBitmessage/releases/latest下载 - + Waiting for PoW to finish... %1% 等待PoW完成...%1% - + Shutting down Pybitmessage... %1% 关闭Pybitmessage ...%1% - + Waiting for objects to be sent... %1% 等待要发送对象...%1% - + Saving settings... %1% 保存设置...%1% - + Shutting down core... %1% 关闭核心...%1% - + Stopping notifications... %1% 停止通知...%1% - + Shutdown imminent... %1% 关闭即将来临...%1% @@ -1157,42 +1157,42 @@ Are you sure you want to delete the channel? %n 小时 - + %n day(s) %n 天 - + Shutting down PyBitmessage... %1% 关闭PyBitmessage...%1% - + Sent 发送 - + Generating one new address 生成一个新的地址 - + Done generating address. Doing work necessary to broadcast it... 完成生成地址. 做必要的工作, 以播放它... - + Generating %1 new addresses. 生成%1个新地址. - + %1 is already in 'Your Identities'. Not adding it again. %1已经在'您的身份'. 不必重新添加. - + Done generating address 完成生成地址 @@ -1202,226 +1202,295 @@ Are you sure you want to delete the channel? - + Disk full 磁盘已满 - + Alert: Your disk or data storage volume is full. Bitmessage will now exit. 警告: 您的磁盘或数据存储量已满. 比特信将立即退出. - + Error! Could not find sender address (your address) in the keys.dat file. 错误! 找不到在keys.dat 件发件人的地址 ( 您的地址). - + Doing work necessary to send broadcast... 做必要的工作, 以发送广播... - + Broadcast sent on %1 广播发送%1 - + Encryption key was requested earlier. 加密密钥已请求. - + Sending a request for the recipient's encryption key. 发送收件人的加密密钥的请求. - + Looking up the receiver's public key 展望接收方的公钥 - + Problem: Destination is a mobile device who requests that the destination be included in the message but this is disallowed in your settings. %1 问题: 目标是移动电话设备所请求的目的地包括在消息中, 但是这是在你的设置禁止. %1 - + Doing work necessary to send message. There is no required difficulty for version 2 addresses like this. 做必要的工作, 以发送信息. 这样第2版的地址没有难度. - + Doing work necessary to send message. Receiver's required difficulty: %1 and %2 做必要的工作, 以发送短信. 接收者的要求难度: %1与%2 - + Problem: The work demanded by the recipient (%1 and %2) is more difficult than you are willing to do. %3 问题: 由接收者(%1%2)要求的工作量比您愿意做的工作量來得更困难. %3 - + Problem: You are trying to send a message to yourself or a chan but your encryption key could not be found in the keys.dat file. Could not encrypt message. %1 问题: 您正在尝试将信息发送给自己或频道, 但您的加密密钥无法在keys.dat文件中找到. 无法加密信息. %1 - + Doing work necessary to send message. 做必要的工作, 以发送信息. - + Message sent. Waiting for acknowledgement. Sent on %1 信息发送. 等待确认. 已发送%1 - + Doing work necessary to request encryption key. 做必要的工作以要求加密密钥. - + Broadcasting the public key request. This program will auto-retry if they are offline. 广播公钥请求. 这个程序将自动重试, 如果他们处于离线状态. - + Sending public key request. Waiting for reply. Requested at %1 发送公钥的请求. 等待回复. 请求在%1 - + UPnP port mapping established on port %1 UPnP端口映射建立在端口%1 - + UPnP port mapping removed UPnP端口映射被删除 - + Mark all messages as read 标记全部信息为已读 - + Are you sure you would like to mark all messages read? 确定将所有信息标记为已读吗? - + Doing work necessary to send broadcast. 持续进行必要的工作,以发送广播。 - + Proof of work pending 待传输内容的校验 - + %n object(s) pending proof of work %n 待传输内容校验任务 - + %n object(s) waiting to be distributed %n 任务等待分配 - + Wait until these tasks finish? 等待所有任务执行完? - + Problem communicating with proxy: %1. Please check your network settings. 与代理通信故障率:%1。请检查你的网络连接。 - + SOCKS5 Authentication problem: %1. Please check your SOCKS5 settings. SOCK5认证错误:%1。请检查你的SOCK5设置。 - + The time on your computer, %1, may be wrong. Please verify your settings. 你电脑上时间有误:%1。请检查你的设置。 - + + The name %1 was not found. + 名字%1未找到。 + + + + The namecoin query failed (%1) + 域名币查询失败(%1) + + + + The namecoin query failed. + 域名币查询失败。 + + + + The name %1 has no valid JSON data. + 名字%1没有有效地JSON数据。 + + + + The name %1 has no associated Bitmessage address. + 名字%1没有关联比特信地址。 + + + + Success! Namecoind version %1 running. + 成功!域名币系统%1运行中。 + + + + Success! NMControll is up and running. + 成功!域名币控制上线运行! + + + + Couldn't understand NMControl. + 不能理解 NMControl。 + + + + Your GPU(s) did not calculate correctly, disabling OpenCL. Please report to the developers. + 你的GPU(s)不能够正确计算,关闭OpenGL。请报告给开发者。 + + + + + Welcome to easy and secure Bitmessage + * send messages to other people + * send broadcast messages like twitter or + * discuss in chan(nel)s with other people + + +欢迎使用简便安全的比特信 +*发送信息给其他人 +*像推特那样发送广播信息 +*在频道(s)里和其他人讨论 + + + + not recommended for chans + 频道内不建议的内容 + + + + Problems connecting? Try enabling UPnP in the Network Settings + 连接问题?请尝试在网络设置里打开UPnP + + + Error: Bitmessage addresses start with BM- Please check the recipient address %1 错误:Bitmessage地址是以BM-开头的,请检查收信地址%1. - + Error: The recipient address %1 is not typed or copied correctly. Please check it. 错误:收信地址%1未填写或复制错误。请检查。 - + Error: The recipient address %1 contains invalid characters. Please check it. 错误:收信地址%1还有非法字符。请检查。 - + Error: The version of the recipient address %1 is too high. Either you need to upgrade your Bitmessage software or your acquaintance is being clever. 错误:收信地址%1版本太高。要么你需要更新你的软件,要么对方需要降级 。 - + Error: Some data encoded in the recipient address %1 is too short. There might be something wrong with the software of your acquaintance. 错误:收信地址%1编码数据太短。可能对方使用的软件有问题。 - + Error: Some data encoded in the recipient address %1 is too long. There might be something wrong with the software of your acquaintance. 错误: - + Error: Some data encoded in the recipient address %1 is malformed. There might be something wrong with the software of your acquaintance. 错误:收信地址%1编码数据太长。可能对方使用的软件有问题。 - + Error: Something is wrong with the recipient address %1. 错误:收信地址%1有问题。 - + Synchronisation pending 待同步 - + Bitmessage hasn't synchronised with the network, %n object(s) to be downloaded. If you quit now, it may cause delivery delays. Wait until the synchronisation finishes? Bitmessage还没有与网络同步,%n 件任务需要下载。如果你现在退出软件,可能会造成传输延时。是否等同步完成? - + Not connected 未连接成功。 - + Bitmessage isn't connected to the network. If you quit now, it may cause delivery delays. Wait until connected and the synchronisation finishes? Bitmessage未连接到网络。如果现在退出软件,可能会造成传输延时。是否等待同步完成? - + Waiting for network connection... 等待网络连接…… - + Waiting for finishing synchronisation... 等待同步完成…… @@ -1452,14 +1521,14 @@ Receiver's required difficulty: %1 and %2 MsgDecode - + The message has an unknown encoding. Perhaps you should upgrade Bitmessage. 这些消息使用了未知编码方式。 你可能需要更新Bitmessage软件。 - + Unknown encoding 未知编码 @@ -1617,27 +1686,27 @@ The 'Random Number' option is selected by default but deterministic ad aboutDialog - + About 关于 - + PyBitmessage PyBitmessage - + version ? 版本 ? - + <html><head/><body><p>Distributed under the MIT/X11 software license; see <a href="http://www.opensource.org/licenses/mit-license.php"><span style=" text-decoration: underline; color:#0000ff;">http://www.opensource.org/licenses/mit-license.php</span></a></p></body></html> <html><head/><body><p>以 MIT/X11 软件授权发布; 详情参见 <a href="http://www.opensource.org/licenses/mit-license.php"><span style=" text-decoration: underline; color:#0000ff;">http://www.opensource.org/licenses/mit-license.php</span></a></p></body></html> - + This is Beta software. 本软件处于Beta阶段。 @@ -1647,7 +1716,7 @@ The 'Random Number' option is selected by default but deterministic ad - + <html><head/><body><p>Copyright &copy; 2012-2016 Jonathan Warren<br/>Copyright &copy; 2013-2016 The Bitmessage Developers</p></body></html> <html><head/><body><p>版权:2012-2016 Jonathan Warren<br/>版权: 2013-2016 The Bitmessage Developers</p></body></html> @@ -1680,12 +1749,12 @@ The 'Random Number' option is selected by default but deterministic ad 地址 - + Blacklist 黑名单 - + Whitelist 白名单 @@ -1767,77 +1836,77 @@ The 'Random Number' option is selected by default but deterministic ad 总连接: - + Since startup: 自启动: - + Processed 0 person-to-person messages. 处理0人对人的信息. - + Processed 0 public keys. 处理0公钥。 - + Processed 0 broadcasts. 处理0广播. - + Inventory lookups per second: 0 每秒库存查询: 0 - + Objects to be synced: 对象 已同步: - + Stream # 数据流 # Connections - 连接 + - + Since startup on %1 自从%1启动 - + Down: %1/s Total: %2 下: %1/秒 总计: %2 - + Up: %1/s Total: %2 上: %1/秒 总计: %2 - + Total Connections: %1 总的连接数: %1 - + Inventory lookups per second: %1 每秒库存查询: %1 - + Up: 0 kB/s 上载: 0 kB /秒 - + Down: 0 kB/s 下载: 0 kB /秒 @@ -1847,30 +1916,75 @@ The 'Random Number' option is selected by default but deterministic ad 网络状态 - + byte(s) 字节 - + Object(s) to be synced: %n 要同步的对象: %n - + Processed %n person-to-person message(s). 处理%n人对人的信息. - + Processed %n broadcast message(s). 处理%n广播信息. - + Processed %n public key(s). 处理%n公钥. + + + Peer + 节点 + + + + IP address or hostname + IP地址或主机名 + + + + Rating + 等级 + + + + PyBitmessage tracks the success rate of connection attempts to individual nodes. The rating ranges from -1 to 1 and affects the likelihood of selecting the node in the future + PyBitmessage 跟踪连接尝试到各个节点的成功率。评分范围从 -1 到 1 ,这会影响到未来选择节点的可能性。 + + + + User agent + 用户代理 + + + + Peer's self-reported software + 节点自我报告的软件 + + + + TLS + TLS + + + + Connection encryption + 连接加密 + + + + List of streams negotiated between you and the peer + 您和节点之间已协商的流列表 + newChanDialog @@ -1953,16 +2067,42 @@ The 'Random Number' option is selected by default but deterministic ad 成功创建或加入频道%1 - + Chan creation / joining failed 频道创建或加入失败 - + Chan creation / joining cancelled 频道创建或加入已取消 + + proofofwork + + + C PoW module built successfully. + C PoW模块编译成功。 + + + + Failed to build C PoW module. Please build it manually. + 无法编译C PoW模块。请手动编译。 + + + + C PoW module unavailable. Please build it. + C PoW模块不可用。请编译它。 + + + + qrcodeDialog + + + QR-code + 二维码 + + regenerateAddressesDialog @@ -2019,218 +2159,218 @@ The 'Random Number' option is selected by default but deterministic ad settingsDialog - + Settings 设置 - + Start Bitmessage on user login 在用户登录时启动比特信 - + Tray 任务栏 - + Start Bitmessage in the tray (don't show main window) 启动比特信到托盘 (不要显示主窗口) - + Minimize to tray 最小化到托盘 - + Close to tray 关闭任务栏 - + Show notification when message received 在收到消息时提示 - + Run in Portable Mode 以便携方式运行 - + In Portable Mode, messages and config files are stored in the same directory as the program rather than the normal application-data folder. This makes it convenient to run Bitmessage from a USB thumb drive. 在便携模式下, 消息和配置文件和程序保存在同一个目录而不是通常的程序数据文件夹。 这使在U盘中允许比特信很方便。 - + Willingly include unencrypted destination address when sending to a mobile device 愿意在发送到手机时使用不加密的目标地址 - + Use Identicons 用户身份 - + Reply below Quote 回复 引述如下 - + Interface Language 界面语言 - + System Settings system 系统设置 - + User Interface 用户界面 - + Listening port 监听端口 - + Listen for connections on port: 监听连接于端口: - + UPnP: UPnP: - + Bandwidth limit 带宽限制 - + Maximum download rate (kB/s): [0: unlimited] 最大下载速率(kB/秒): [0: 无限制] - + Maximum upload rate (kB/s): [0: unlimited] 最大上传速度 (kB/秒): [0: 无限制] - + Proxy server / Tor 代理服务器 / Tor - + Type: 类型: - + Server hostname: 服务器主机名: - + Port: 端口: - + Authentication 认证 - + Username: 用户名: - + Pass: 密码: - + Listen for incoming connections when using proxy 在使用代理时仍然监听入站连接 - + none - + SOCKS4a SOCKS4a - + SOCKS5 SOCKS5 - + Network Settings 网络设置 - + Total difficulty: 总难度: - + The 'Total difficulty' affects the absolute amount of work the sender must complete. Doubling this value doubles the amount of work. “总难度”影响发送者所需要的做工总数。当这个值翻倍时,做工的总数也翻倍。 - + Small message difficulty: 小消息难度: - + When someone sends you a message, their computer must first complete some work. The difficulty of this work, by default, is 1. You may raise this default for new addresses you create by changing the values here. Any new addresses you create will require senders to meet the higher difficulty. There is one exception: if you add a friend or acquaintance to your address book, Bitmessage will automatically notify them when you next send a message that they need only complete the minimum amount of work: difficulty 1. 当一个人向您发送消息的时候, 他们的电脑必须先做工。这个难度的默认值是1,您可以在创建新的地址前提高这个值。任何新创建的地址都会要求更高的做工量。这里有一个例外,当您将您的朋友添加到地址本的时候,比特信将自动提示他们,当他们下一次向您发送的时候,他们需要的做功量将总是1. - + The 'Small message difficulty' mostly only affects the difficulty of sending small messages. Doubling this value makes it almost twice as difficult to send a small message but doesn't really affect large messages. “小消息困难度”几乎仅影响发送消息。当这个值翻倍时,发小消息时做工的总数也翻倍,但是并不影响大的消息。 - + Demanded difficulty 要求的难度 - + Here you may set the maximum amount of work you are willing to do to send a message to another person. Setting these values to 0 means that any value is acceptable. 你可以在这里设置您所愿意接受的发送消息的最大难度。0代表接受任何难度。 - + Maximum acceptable total difficulty: 最大接受难度: - + Maximum acceptable small message difficulty: 最大接受的小消息难度: - + Max acceptable difficulty 最大可接受难度 @@ -2240,82 +2380,87 @@ The 'Random Number' option is selected by default but deterministic ad - + <html><head/><body><p>Bitmessage can utilize a different Bitcoin-based program called Namecoin to make addresses human-friendly. For example, instead of having to tell your friend your long Bitmessage address, you can simply tell him to send a message to <span style=" font-style:italic;">test. </span></p><p>(Getting your own Bitmessage address into Namecoin is still rather difficult).</p><p>Bitmessage can use either namecoind directly or a running nmcontrol instance.</p></body></html> <html><head/><body><p>比特信可以利用基于比特币的Namecoin让地址更加友好。比如除了告诉您的朋友您的长长的比特信地址,您还可以告诉他们发消息给 <span style=" font-style:italic;">test. </span></p><p>把您的地址放入Namecoin还是相当的难的.</p><p>比特信可以不但直接连接到namecoin守护程序或者连接到运行中的nmcontrol实例.</p></body></html> - + Host: 主机名: - + Password: 密码: - + Test 测试 - + Connect to: 连接到: - + Namecoind Namecoind - + NMControl NMControl - + Namecoin integration Namecoin整合 - + <html><head/><body><p>By default, if you send a message to someone and he is offline for more than two days, Bitmessage will send the message again after an additional two days. This will be continued with exponential backoff forever; messages will be resent after 5, 10, 20 days ect. until the receiver acknowledges them. Here you may change that behavior by having Bitmessage give up after a certain number of days or months.</p><p>Leave these input fields blank for the default behavior. </p></body></html> <html><head/><body><p>您发给他们的消息默认会在网络上保存两天,之后比特信会再重发一次. 重发时间会随指数上升; 消息会在5, 10, 20... 天后重发并以此类推. 直到收到收件人的回执. 你可以在这里改变这一行为,让比特信在尝试一段时间后放弃.</p><p>留空意味着默认行为. </p></body></html> - + Give up after - + and - + days - + months. 月后放弃。 - + Resends Expire 重发超时 - + Hide connection notifications 隐藏连接通知 - + + Maximum outbound connections: [0: none] + 最大外部连接:[0: 无] + + + Hardware GPU acceleration (OpenCL): 硬件GPU加速(OpenCL): From 58b47bc6de9816bf8c74e6a597d3fa1d28a15436 Mon Sep 17 00:00:00 2001 From: Peter Surda Date: Tue, 15 Aug 2017 12:22:24 +0200 Subject: [PATCH 187/407] Forking fixes --- src/bitmessagemain.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/bitmessagemain.py b/src/bitmessagemain.py index eed725d3..426af695 100755 --- a/src/bitmessagemain.py +++ b/src/bitmessagemain.py @@ -328,7 +328,7 @@ class Main: def daemonize(self): try: if os.fork(): - exit(0) + os._exit(0) except AttributeError: # fork not implemented pass @@ -342,7 +342,7 @@ class Main: pass try: if os.fork(): - exit(0) + os._exit(0) except AttributeError: # fork not implemented pass From a48dff3bee03bb37b2893372aba7563e3b97433c Mon Sep 17 00:00:00 2001 From: Peter Surda Date: Tue, 15 Aug 2017 12:24:43 +0200 Subject: [PATCH 188/407] PoW init reordering - inited by the worker thread on its own init, instead of when the imports are being evaluated - also got rid of windows-style newlines in OpenCL PoW --- src/class_singleWorker.py | 1 + src/openclpow.py | 216 +++++++++++++++++++------------------- src/proofofwork.py | 9 +- 3 files changed, 114 insertions(+), 112 deletions(-) diff --git a/src/class_singleWorker.py b/src/class_singleWorker.py index e7f58f1b..58eb33c6 100644 --- a/src/class_singleWorker.py +++ b/src/class_singleWorker.py @@ -42,6 +42,7 @@ class singleWorker(threading.Thread, StoppableThread): # QThread.__init__(self, parent) threading.Thread.__init__(self, name="singleWorker") self.initStop() + proofofwork.init() def stopThread(self): try: diff --git a/src/openclpow.py b/src/openclpow.py index ca40e634..894a5b77 100644 --- a/src/openclpow.py +++ b/src/openclpow.py @@ -1,111 +1,111 @@ -#!/usr/bin/env python2.7 -from struct import pack, unpack -import time -import hashlib -import random -import os - -from bmconfigparser import BMConfigParser -import paths -from state import shutdown -from debug import logger - -libAvailable = True -ctx = False -queue = False -program = False -gpus = [] -enabledGpus = [] -vendors = [] -hash_dt = None - -try: - import numpy - import pyopencl as cl -except: - libAvailable = False - -def initCL(): +#!/usr/bin/env python2.7 +from struct import pack, unpack +import time +import hashlib +import random +import os + +from bmconfigparser import BMConfigParser +import paths +from state import shutdown +from debug import logger + +libAvailable = True +ctx = False +queue = False +program = False +gpus = [] +enabledGpus = [] +vendors = [] +hash_dt = None + +try: + import numpy + import pyopencl as cl +except: + libAvailable = False + +def initCL(): global ctx, queue, program, hash_dt, libAvailable if libAvailable is False: return - del enabledGpus[:] - del vendors[:] - del gpus[:] - ctx = False - try: - hash_dt = numpy.dtype([('target', numpy.uint64), ('v', numpy.str_, 73)]) - try: - for platform in cl.get_platforms(): - gpus.extend(platform.get_devices(device_type=cl.device_type.GPU)) - if BMConfigParser().safeGet("bitmessagesettings", "opencl") == platform.vendor: - enabledGpus.extend(platform.get_devices(device_type=cl.device_type.GPU)) - if platform.vendor not in vendors: - vendors.append(platform.vendor) - except: - pass - if (len(enabledGpus) > 0): - ctx = cl.Context(devices=enabledGpus) - queue = cl.CommandQueue(ctx) - f = open(os.path.join(paths.codePath(), "bitmsghash", 'bitmsghash.cl'), 'r') - fstr = ''.join(f.readlines()) - program = cl.Program(ctx, fstr).build(options="") - logger.info("Loaded OpenCL kernel") - else: - logger.info("No OpenCL GPUs found") - del enabledGpus[:] - except Exception as e: - logger.error("OpenCL fail: ", exc_info=True) - del enabledGpus[:] - -def openclAvailable(): - return (len(gpus) > 0) - -def openclEnabled(): - return (len(enabledGpus) > 0) - -def do_opencl_pow(hash, target): - output = numpy.zeros(1, dtype=[('v', numpy.uint64, 1)]) - if (len(enabledGpus) == 0): - return output[0][0] - - data = numpy.zeros(1, dtype=hash_dt, order='C') - data[0]['v'] = ("0000000000000000" + hash).decode("hex") - data[0]['target'] = target - - hash_buf = cl.Buffer(ctx, cl.mem_flags.READ_ONLY | cl.mem_flags.COPY_HOST_PTR, hostbuf=data) - dest_buf = cl.Buffer(ctx, cl.mem_flags.WRITE_ONLY, output.nbytes) - - kernel = program.kernel_sha512 - worksize = kernel.get_work_group_info(cl.kernel_work_group_info.WORK_GROUP_SIZE, enabledGpus[0]) - - kernel.set_arg(0, hash_buf) - kernel.set_arg(1, dest_buf) - - start = time.time() - progress = 0 - globamt = worksize*2000 - - while output[0][0] == 0 and shutdown == 0: - kernel.set_arg(2, pack("Q',hashlib.sha512(hashlib.sha512(pack('>Q',nonce) + initialHash).digest()).digest()[0:8]) - print "{} - value {} < {}".format(nonce, trialValue, target) - + del enabledGpus[:] + del vendors[:] + del gpus[:] + ctx = False + try: + hash_dt = numpy.dtype([('target', numpy.uint64), ('v', numpy.str_, 73)]) + try: + for platform in cl.get_platforms(): + gpus.extend(platform.get_devices(device_type=cl.device_type.GPU)) + if BMConfigParser().safeGet("bitmessagesettings", "opencl") == platform.vendor: + enabledGpus.extend(platform.get_devices(device_type=cl.device_type.GPU)) + if platform.vendor not in vendors: + vendors.append(platform.vendor) + except: + pass + if (len(enabledGpus) > 0): + ctx = cl.Context(devices=enabledGpus) + queue = cl.CommandQueue(ctx) + f = open(os.path.join(paths.codePath(), "bitmsghash", 'bitmsghash.cl'), 'r') + fstr = ''.join(f.readlines()) + program = cl.Program(ctx, fstr).build(options="") + logger.info("Loaded OpenCL kernel") + else: + logger.info("No OpenCL GPUs found") + del enabledGpus[:] + except Exception as e: + logger.error("OpenCL fail: ", exc_info=True) + del enabledGpus[:] + +def openclAvailable(): + return (len(gpus) > 0) + +def openclEnabled(): + return (len(enabledGpus) > 0) + +def do_opencl_pow(hash, target): + output = numpy.zeros(1, dtype=[('v', numpy.uint64, 1)]) + if (len(enabledGpus) == 0): + return output[0][0] + + data = numpy.zeros(1, dtype=hash_dt, order='C') + data[0]['v'] = ("0000000000000000" + hash).decode("hex") + data[0]['target'] = target + + hash_buf = cl.Buffer(ctx, cl.mem_flags.READ_ONLY | cl.mem_flags.COPY_HOST_PTR, hostbuf=data) + dest_buf = cl.Buffer(ctx, cl.mem_flags.WRITE_ONLY, output.nbytes) + + kernel = program.kernel_sha512 + worksize = kernel.get_work_group_info(cl.kernel_work_group_info.WORK_GROUP_SIZE, enabledGpus[0]) + + kernel.set_arg(0, hash_buf) + kernel.set_arg(1, dest_buf) + + start = time.time() + progress = 0 + globamt = worksize*2000 + + while output[0][0] == 0 and shutdown == 0: + kernel.set_arg(2, pack("Q',hashlib.sha512(hashlib.sha512(pack('>Q',nonce) + initialHash).digest()).digest()[0:8]) + print "{} - value {} < {}".format(nonce, trialValue, target) + diff --git a/src/proofofwork.py b/src/proofofwork.py index eb845c25..12d513b6 100644 --- a/src/proofofwork.py +++ b/src/proofofwork.py @@ -237,6 +237,9 @@ def resetPoW(): # init def init(): global bitmsglib, bso, bmpow + + openclpow.initCL() + if "win32" == sys.platform: if ctypes.sizeof(ctypes.c_voidp) == 4: bitmsglib = 'bitmsghash32.dll' @@ -286,7 +289,5 @@ def init(): bmpow = None else: bmpow = None - -init() -if bmpow is None: - buildCPoW() + if bmpow is None: + buildCPoW() From 623553393b640cb459262053bd73cd3ddc320810 Mon Sep 17 00:00:00 2001 From: Peter Surda Date: Tue, 15 Aug 2017 12:25:38 +0200 Subject: [PATCH 189/407] PID file truncate fix - on unix it truncated the file if a second instance was being launched --- src/singleinstance.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/singleinstance.py b/src/singleinstance.py index 78a0cb33..7a025945 100644 --- a/src/singleinstance.py +++ b/src/singleinstance.py @@ -56,7 +56,7 @@ class singleinstance: pidLine = "%i\n" % self.lockPid os.write(self.fd, pidLine) else: # non Windows - self.fp = open(self.lockfile, 'w') + self.fp = open(self.lockfile, 'a+') try: if self.daemon and self.lockPid != os.getpid(): fcntl.lockf(self.fp, fcntl.LOCK_EX) # wait for parent to finish @@ -68,6 +68,7 @@ class singleinstance: sys.exit(-1) else: pidLine = "%i\n" % self.lockPid + self.fp.truncate(0) self.fp.write(pidLine) self.fp.flush() From 8b3d7ea2780dbd1ddc23dd0fc3e27f7824ce04d3 Mon Sep 17 00:00:00 2001 From: Peter Surda Date: Tue, 15 Aug 2017 14:09:19 +0200 Subject: [PATCH 190/407] C PoW init fix --- src/proofofwork.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/proofofwork.py b/src/proofofwork.py index 12d513b6..df6ed295 100644 --- a/src/proofofwork.py +++ b/src/proofofwork.py @@ -19,6 +19,8 @@ import state bitmsglib = 'bitmsghash.so' +bmpow = None + def _set_idle(): if 'linux' in sys.platform: os.nice(20) From 2da4d007303afead1028f7d468b2a890af5c0900 Mon Sep 17 00:00:00 2001 From: Peter Surda Date: Tue, 15 Aug 2017 18:14:36 +0200 Subject: [PATCH 191/407] Support message C PoW fix --- src/bitmessageqt/support.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/bitmessageqt/support.py b/src/bitmessageqt/support.py index 67e46500..8ec3c16d 100644 --- a/src/bitmessageqt/support.py +++ b/src/bitmessageqt/support.py @@ -13,7 +13,7 @@ from helper_sql import * from l10n import getTranslationLanguage from openclpow import openclAvailable, openclEnabled import paths -from proofofwork import bmpow +import proofofwork from pyelliptic.openssl import OpenSSL import queues import shared @@ -111,7 +111,7 @@ def createSupportMessage(myapp): if paths.frozen: frozen = paths.frozen portablemode = "True" if state.appdata == paths.lookupExeFolder() else "False" - cpow = "True" if bmpow else "False" + cpow = "True" if proofofwork.bmpow else "False" #cpow = QtGui.QApplication.translate("Support", cpow) openclpow = str(BMConfigParser().safeGet('bitmessagesettings', 'opencl')) if openclEnabled() else "None" #openclpow = QtGui.QApplication.translate("Support", openclpow) From 314af0925f0514ce84f297877f4347bd1d767668 Mon Sep 17 00:00:00 2001 From: Peter Surda Date: Sun, 20 Aug 2017 11:55:54 +0200 Subject: [PATCH 192/407] Daemonize fix for Windows - /dev/null isn't available on Windows so just close the console sockets directly --- src/bitmessagemain.py | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/src/bitmessagemain.py b/src/bitmessagemain.py index 426af695..62e79371 100755 --- a/src/bitmessagemain.py +++ b/src/bitmessagemain.py @@ -351,12 +351,18 @@ class Main: shared.thisapp.lockPid = None # indicate we're the final child sys.stdout.flush() sys.stderr.flush() - si = file('/dev/null', 'r') - so = file('/dev/null', 'a+') - se = file('/dev/null', 'a+', 0) - os.dup2(si.fileno(), sys.stdin.fileno()) - os.dup2(so.fileno(), sys.stdout.fileno()) - os.dup2(se.fileno(), sys.stderr.fileno()) + try: + si = file('/dev/null', 'r') + so = file('/dev/null', 'a+') + se = file('/dev/null', 'a+', 0) + os.dup2(si.fileno(), sys.stdin.fileno()) + os.dup2(so.fileno(), sys.stdout.fileno()) + os.dup2(se.fileno(), sys.stderr.fileno()) + # /dev/null not available + except IOError: + sys.stdin.close() + sys.stdout.close() + sys.stderr.close() def setSignalHandler(self): signal.signal(signal.SIGINT, helper_generic.signal_handler) From b886f935d465eb8dd42537117746185556dc83c7 Mon Sep 17 00:00:00 2001 From: Peter Surda Date: Sun, 20 Aug 2017 12:05:53 +0200 Subject: [PATCH 193/407] Daemon Windows fix - closing the filehandle isn't the correct approach, it causes more bugs. Use os.devnull instead --- src/bitmessagemain.py | 18 ++++++------------ 1 file changed, 6 insertions(+), 12 deletions(-) diff --git a/src/bitmessagemain.py b/src/bitmessagemain.py index 62e79371..894c9e08 100755 --- a/src/bitmessagemain.py +++ b/src/bitmessagemain.py @@ -351,18 +351,12 @@ class Main: shared.thisapp.lockPid = None # indicate we're the final child sys.stdout.flush() sys.stderr.flush() - try: - si = file('/dev/null', 'r') - so = file('/dev/null', 'a+') - se = file('/dev/null', 'a+', 0) - os.dup2(si.fileno(), sys.stdin.fileno()) - os.dup2(so.fileno(), sys.stdout.fileno()) - os.dup2(se.fileno(), sys.stderr.fileno()) - # /dev/null not available - except IOError: - sys.stdin.close() - sys.stdout.close() - sys.stderr.close() + si = file(os.devnull, 'r') + so = file(os.devnull, 'a+') + se = file(os.devnull, 'a+', 0) + os.dup2(si.fileno(), sys.stdin.fileno()) + os.dup2(so.fileno(), sys.stdout.fileno()) + os.dup2(se.fileno(), sys.stderr.fileno()) def setSignalHandler(self): signal.signal(signal.SIGINT, helper_generic.signal_handler) From b7f808cde18c503502977cac679ea00682d4ab53 Mon Sep 17 00:00:00 2001 From: Peter Surda Date: Mon, 21 Aug 2017 10:39:03 +0200 Subject: [PATCH 194/407] Add shutdown command to API - calling "shutdown" now cleanly shuts down PyBitmessage, however the call may not return so you need to add an error handler to the call. With python for example, wrap the "shutdown()" in "try:/except socket.error" --- src/api.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/api.py b/src/api.py index dcc83538..05b27432 100644 --- a/src/api.py +++ b/src/api.py @@ -30,6 +30,7 @@ import protocol import state from pyelliptic.openssl import OpenSSL import queues +import shutdown from struct import pack import network.stats @@ -982,6 +983,10 @@ class MySimpleXMLRPCRequestHandler(SimpleXMLRPCRequestHandler): sqlStoredProcedure('deleteandvacuume') return 'done' + def HandleShutdown(self, params): + shutdown.doCleanShutdown() + return 'done' + handlers = {} handlers['helloWorld'] = HandleHelloWorld handlers['add'] = HandleAdd @@ -1032,6 +1037,7 @@ class MySimpleXMLRPCRequestHandler(SimpleXMLRPCRequestHandler): handlers['clientStatus'] = HandleClientStatus handlers['decodeAddress'] = HandleDecodeAddress handlers['deleteAndVacuum'] = HandleDeleteAndVacuum + handlers['shutdown'] = HandleShutdown def _handle_request(self, method, params): if (self.handlers.has_key(method)): From 18119339f8d1fab0ae150e6ce98c4bb161089f67 Mon Sep 17 00:00:00 2001 From: Peter Surda Date: Tue, 22 Aug 2017 13:23:03 +0200 Subject: [PATCH 195/407] Add shutdown to CLI --- src/bitmessagecli.py | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/src/bitmessagecli.py b/src/bitmessagecli.py index 068a5597..ee877818 100644 --- a/src/bitmessagecli.py +++ b/src/bitmessagecli.py @@ -12,6 +12,7 @@ import getopt import imghdr import ntpath import json +import socket import time import sys import os @@ -1283,6 +1284,13 @@ def clientStatus(): print "\nnumberOfMessagesProcessed: " + str(clientStatus['numberOfMessagesProcessed']) + "\n" print "\nnumberOfBroadcastsProcessed: " + str(clientStatus['numberOfBroadcastsProcessed']) + "\n" +def shutdown(): + try: + api.shutdown() + except socket.error: + pass + print "\nShutdown command relayed\n" + def UI(usrInput): #Main user menu global usrPrompt @@ -1705,6 +1713,11 @@ def UI(usrInput): #Main user menu usrPrompt = 1 main() + elif usrInput == "shutdown": + shutdown() + usrPrompt = 1 + main() + elif usrInput == "million+": genMilAddr() usrPrompt = 1 From 660997b8f4a94a21a73d050134e7e886dee12b9c Mon Sep 17 00:00:00 2001 From: Peter Surda Date: Tue, 22 Aug 2017 13:49:27 +0200 Subject: [PATCH 196/407] Code Quality --- src/api.py | 13 ++++++----- src/bitmessagemain.py | 2 -- src/network/advanceddispatcher.py | 3 --- src/network/asyncore_pollchoose.py | 37 +++++++++++++++--------------- src/network/bmproto.py | 8 ++----- src/network/connectionchooser.py | 10 ++++---- src/network/stats.py | 21 +++++++++-------- 7 files changed, 45 insertions(+), 49 deletions(-) diff --git a/src/api.py b/src/api.py index 05b27432..e20854fc 100644 --- a/src/api.py +++ b/src/api.py @@ -26,7 +26,6 @@ import helper_inbox import helper_sent import hashlib -import protocol import state from pyelliptic.openssl import OpenSSL import queues @@ -980,12 +979,14 @@ class MySimpleXMLRPCRequestHandler(SimpleXMLRPCRequestHandler): queues.UISignalQueue.put(('updateStatusBar', message)) def HandleDeleteAndVacuum(self, params): - sqlStoredProcedure('deleteandvacuume') - return 'done' + if not params: + sqlStoredProcedure('deleteandvacuume') + return 'done' def HandleShutdown(self, params): - shutdown.doCleanShutdown() - return 'done' + if not params: + shutdown.doCleanShutdown() + return 'done' handlers = {} handlers['helloWorld'] = HandleHelloWorld @@ -1041,7 +1042,7 @@ class MySimpleXMLRPCRequestHandler(SimpleXMLRPCRequestHandler): def _handle_request(self, method, params): if (self.handlers.has_key(method)): - return self.handlers[method](self ,params) + return self.handlers[method](self, params) else: raise APIError(20, 'Invalid method: %s' % method) diff --git a/src/bitmessagemain.py b/src/bitmessagemain.py index 894c9e08..1d37123f 100755 --- a/src/bitmessagemain.py +++ b/src/bitmessagemain.py @@ -44,8 +44,6 @@ import threading from class_sqlThread import sqlThread from class_singleCleaner import singleCleaner from class_objectProcessor import objectProcessor -from class_outgoingSynSender import outgoingSynSender -from class_singleListener import singleListener from class_singleWorker import singleWorker from class_addressGenerator import addressGenerator from class_smtpDeliver import smtpDeliver diff --git a/src/network/advanceddispatcher.py b/src/network/advanceddispatcher.py index 005aa038..eb636aed 100644 --- a/src/network/advanceddispatcher.py +++ b/src/network/advanceddispatcher.py @@ -1,6 +1,4 @@ -import Queue import socket -import sys import threading import time @@ -43,7 +41,6 @@ class AdvancedDispatcher(asyncore.dispatcher): def process(self): if not self.connected: return False - loop = 0 while True: try: with nonBlocking(self.processingLock): diff --git a/src/network/asyncore_pollchoose.py b/src/network/asyncore_pollchoose.py index c2568e9f..caa9d650 100644 --- a/src/network/asyncore_pollchoose.py +++ b/src/network/asyncore_pollchoose.py @@ -137,7 +137,7 @@ def set_rates(download, upload): uploadTimestamp = time.time() def update_received(download=0): - global receivedBytes, maxDownloadRate, downloadBucket, downloadTimestamp + global receivedBytes, downloadBucket, downloadTimestamp currentTimestamp = time.time() receivedBytes += download if maxDownloadRate > 0: @@ -149,7 +149,7 @@ def update_received(download=0): downloadTimestamp = currentTimestamp def update_sent(upload=0): - global sentBytes, maxUploadRate, uploadBucket, uploadTimestamp + global sentBytes, uploadBucket, uploadTimestamp currentTimestamp = time.time() sentBytes += upload if maxUploadRate > 0: @@ -349,14 +349,14 @@ def kqueue_poller(timeout=0.0, map=None): flags = select.KQ_EV_ADD | select.KQ_EV_ENABLE selectables = 0 for fd, obj in map.items(): - filter = 0 + kq_filter = 0 if obj.readable(): - filter |= select.KQ_FILTER_READ + kq_filter |= select.KQ_FILTER_READ if obj.writable(): - filter |= select.KQ_FILTER_WRITE - if filter: + kq_filter |= select.KQ_FILTER_WRITE + if kq_filter: try: - ev = select.kevent(fd, filter=filter, flags=flags) + ev = select.kevent(fd, filter=kq_filter, flags=flags) kqueue.control([ev], 0) selectables += 1 except IOError: @@ -383,9 +383,10 @@ def loop(timeout=30.0, use_poll=False, map=None, count=None, # argument which should no longer be used in favor of # "poller" - if poller is None: - if hasattr(select, 'epoll'): + if use_poll: + poller = poll_poller + elif hasattr(select, 'epoll'): poller = epoll_poller elif hasattr(select, 'kqueue'): poller = kqueue_poller @@ -506,9 +507,9 @@ class dispatcher: # no poll used, or not registered pass - def create_socket(self, family=socket.AF_INET, type=socket.SOCK_STREAM): - self.family_and_type = family, type - sock = socket.socket(family, type) + def create_socket(self, family=socket.AF_INET, socket_type=socket.SOCK_STREAM): + self.family_and_type = family, socket_type + sock = socket.socket(family, socket_type) sock.setblocking(0) self.set_socket(sock) @@ -652,9 +653,9 @@ class dispatcher: def log(self, message): sys.stderr.write('log: %s\n' % str(message)) - def log_info(self, message, type='info'): - if type not in self.ignore_log_types: - print('%s: %s' % (type, message)) + def log_info(self, message, log_type='info'): + if log_type not in self.ignore_log_types: + print('%s: %s' % (log_type, message)) def handle_read_event(self): if self.accepting: @@ -744,7 +745,7 @@ class dispatcher: def handle_accepted(self, sock, addr): sock.close() - self.log_info('unhandled accepted event', 'warning') + self.log_info('unhandled accepted event on %s' % (addr), 'warning') def handle_close(self): self.log_info('unhandled close event', 'warning') @@ -808,8 +809,8 @@ def close_all(map=None, ignore_all=False): for x in list(map.values()): try: x.close() - except OSError as x: - if x.args[0] == EBADF: + except OSError as e: + if e.args[0] == EBADF: pass elif not ignore_all: raise diff --git a/src/network/bmproto.py b/src/network/bmproto.py index 30068d6d..dbdc26d2 100644 --- a/src/network/bmproto.py +++ b/src/network/bmproto.py @@ -1,26 +1,22 @@ import base64 -from binascii import hexlify import hashlib -import math import time import socket import struct -import sys -from addresses import calculateInventoryHash from bmconfigparser import BMConfigParser from debug import logger from inventory import Inventory import knownnodes from network.advanceddispatcher import AdvancedDispatcher -from network.bmobject import BMObject, BMObjectInsufficientPOWError, BMObjectInvalidDataError, BMObjectExpiredError, BMObjectUnwantedStreamError, BMObjectInvalidError, BMObjectAlreadyHaveError +from network.bmobject import BMObject, BMObjectInsufficientPOWError, BMObjectInvalidDataError, \ + BMObjectExpiredError, BMObjectUnwantedStreamError, BMObjectInvalidError, BMObjectAlreadyHaveError import network.connectionpool from network.node import Node from network.objectracker import ObjectTracker from network.proxy import Proxy, ProxyError, GeneralProxyError import addresses -from bmconfigparser import BMConfigParser from queues import objectProcessorQueue, portCheckerQueue, invQueue, addrQueue import shared import state diff --git a/src/network/connectionchooser.py b/src/network/connectionchooser.py index 681291ad..ee2a8b40 100644 --- a/src/network/connectionchooser.py +++ b/src/network/connectionchooser.py @@ -6,7 +6,7 @@ import knownnodes from queues import portCheckerQueue import state -def getDiscoveredPeer(stream): +def getDiscoveredPeer(): try: peer = random.choice(state.discoveredPeers.keys()) except (IndexError, KeyError): @@ -27,9 +27,11 @@ def chooseConnection(stream): return retval except Queue.Empty: pass - if random.choice((False, True)): - return getDiscoveredPeer(stream) - for i in range(50): + # with a probability of 0.5, connect to a discovered peer + if random.choice((False, True)) and not haveOnion: + # discovered peers are already filtered by allowed streams + return getDiscoveredPeer() + for _ in range(50): peer = random.choice(knownnodes.knownNodes[stream].keys()) try: rating = knownnodes.knownNodes[stream][peer]["rating"] diff --git a/src/network/stats.py b/src/network/stats.py index 45961ac1..ade56ac0 100644 --- a/src/network/stats.py +++ b/src/network/stats.py @@ -1,11 +1,7 @@ import time -from bmconfigparser import BMConfigParser from network.connectionpool import BMConnectionPool -from inventory import PendingDownloadQueue, PendingUpload import asyncore_pollchoose as asyncore -import shared -import throttle lastReceivedTimestamp = time.time() lastReceivedBytes = 0 @@ -16,7 +12,8 @@ currentSentSpeed = 0 def connectedHostsList(): retval = [] - for i in BMConnectionPool().inboundConnections.values() + BMConnectionPool().outboundConnections.values(): + for i in BMConnectionPool().inboundConnections.values() + \ + BMConnectionPool().outboundConnections.values(): if not i.fullyEstablished: continue try: @@ -46,22 +43,26 @@ def downloadSpeed(): currentTimestamp = time.time() if int(lastReceivedTimestamp) < int(currentTimestamp): currentReceivedBytes = asyncore.receivedBytes - currentReceivedSpeed = int((currentReceivedBytes - lastReceivedBytes) / (currentTimestamp - lastReceivedTimestamp)) + currentReceivedSpeed = int((currentReceivedBytes - lastReceivedBytes) / + (currentTimestamp - lastReceivedTimestamp)) lastReceivedBytes = currentReceivedBytes lastReceivedTimestamp = currentTimestamp return currentReceivedSpeed def pendingDownload(): tmp = {} - for connection in BMConnectionPool().inboundConnections.values() + BMConnectionPool().outboundConnections.values(): + for connection in BMConnectionPool().inboundConnections.values() + \ + BMConnectionPool().outboundConnections.values(): for k in connection.objectsNewToMe.keys(): tmp[k] = True return len(tmp) def pendingUpload(): - return 0 tmp = {} - for connection in BMConnectionPool().inboundConnections.values() + BMConnectionPool().outboundConnections.values(): + for connection in BMConnectionPool().inboundConnections.values() + \ + BMConnectionPool().outboundConnections.values(): for k in connection.objectsNewToThem.keys(): tmp[k] = True - return len(tmp) + #This probably isn't the correct logic so it's disabled + #return len(tmp) + return 0 From 7e35ea6bdffe4ff815aab03d4876de68815ea3a6 Mon Sep 17 00:00:00 2001 From: Peter Surda Date: Thu, 24 Aug 2017 14:16:37 +0200 Subject: [PATCH 197/407] Email gateway autoregistration fixes - don't treat "@" in label as an email address - ask for confirmation before autoregistering. It confused some newbies into thinking that bitmessage requires payment --- src/bitmessageqt/__init__.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/bitmessageqt/__init__.py b/src/bitmessageqt/__init__.py index 9f4f0b04..014831bf 100644 --- a/src/bitmessageqt/__init__.py +++ b/src/bitmessageqt/__init__.py @@ -1968,12 +1968,16 @@ class MyForm(settingsmixin.SMainWindow): if "<" in toAddress and ">" in toAddress: toAddress = toAddress.split('<')[1].split('>')[0] # email address - elif toAddress.find("@") >= 0: + if toAddress.find("@") >= 0: if isinstance(acct, GatewayAccount): acct.createMessage(toAddress, fromAddress, subject, message) subject = acct.subject toAddress = acct.toAddress else: + if QtGui.QMessageBox.question(self, "Sending an email?", _translate("MainWindow", + "You are trying to send an email instead of a bitmessage. This requires registering with a gateway. Attempt to register?"), + QtGui.QMessageBox.Yes|QtGui.QMessageBox.No) != QtGui.QMessageBox.Yes: + continue email = acct.getLabel() if email[-14:] != "@mailchuck.com": #attempt register # 12 character random email address From a8e5ea18a623b6de78b15f24b1bcadd958b642c8 Mon Sep 17 00:00:00 2001 From: PyBitmessage Translations Date: Thu, 24 Aug 2017 21:30:54 +0200 Subject: [PATCH 198/407] Auto-updated language eo from transifex --- src/translations/bitmessage_eo.qm | Bin 87724 -> 88118 bytes src/translations/bitmessage_eo.ts | 231 +++++++++++++++--------------- 2 files changed, 118 insertions(+), 113 deletions(-) diff --git a/src/translations/bitmessage_eo.qm b/src/translations/bitmessage_eo.qm index 69c28969c621850702ee10caacc3114925c62cb1..e2ac21b2aa70225463a742effbc36a26f4194b0b 100644 GIT binary patch delta 2812 zcmYjSeO!%aAOBwGKJWK^&Y@ytrg&i9JxJU90aI8H{{9@Y z%{j44cPebnskAS8)^se3v zCT)c4(v85*Mz~iL0S5-c^KusuHys9L7*G)jA6NRWbSHezyMev2A0xk~eKteDrgC6k zE8c8a2@cn%h;GbX(y9^1GWQT)E8fXDPV~0+;Emr9y>1cM=oN^uEF*l5S%=BJzcb=8 zL%_yMF-N|R!oMJHVWZy}u^Ve=Iz0} z1y7*yCJUJV0Ql`Y7LeHi?AXCZ<&}Yj*u)h2?3S4&Knr9Pv!oYf_Tm?o8cf{RPh)H2 z{eTnkY)f@5Fer{~t0Hpa!o?GkFv$<`;!BCa_1PrJn41Ma@)pU&e8R~@<1@Oa6+MslwHGVE`AOSN#t~^Ns?cxIp@!B z10Rgy^xMN=N7W6^GpGVgHkunAc@X&L8qWW6H5Fzv7bwsHnr?2iH4kh^5jWPT2aA+) zS4eJ#|2 z1^&UE|A_XBc5?L!vmNkJI2M%5Lhi>8jldKgce`vdWx<fAO0-6>MtJgS+1RB7M6vA_c#>DVLGIQ|c$k;Y2O>KN&yx=vECLYm-QLIm@q z=7WCJ66w-po|J4|E?w;1XBi;-LTXEs0nZ*uvtLF67qi87X_(q6NcwHtP_U6x#6Ve? z&!TFeceg>)d&@N9|K%{q1lUsiZ z0ET@m&nl*x3J#HfxP(NAo+;lRbq8#+AjZqx?N@imk2thz4U(UY*8*cYV#{Gje$3S$uWxJz7-@@rK0$b5#n?Cpn&_k75fUQqA<4yy@vGQ zjfp*Y-=;V+g{YO>Q=ASg2Mf;;rzs3kCmNJN@`qqSb;_WJ9l)AZ${ArZX|Gboo-6^p zHY>9y_))XoSFWpwqpVy~ZW=~u{o$}uJf#J`DN`PwH4b0@x!zc%e6k`PxN20n z+?_#9>Z2O)4-z(LuGmLu@Ob)2H5J{!p)6Hww;D`4QI%<|rI?Sa3Z8WVUB8R*O2aVe z8r3H&&jGbk)y_~dZ*5Uke@f{y?NQYQxPs}HtLk172W_QTr8L-O=hb{VB^~u@=V{a< zy^pG$%_OJfPqhbYp)5D54J)qz?WfeyYxe^MU#b(!D0Yt$wYhC7r6*dQlt}JEo7IbM z2GUd>@CsSK75bFNb z>NEM&beqqpFML3GOKeyFI_C~Gp{u(6BMNM2r5Z6j~| zx(g^D&QI^I1RM33fA9TqbgTIB>zA#hui}Id9N$=K1RD^+H!bY|8*`d(p0oup)bedkRIly1e4CyIx_6G? zA58%ljTRz&9m1Uy-a5AO8Un`U(L;(#8#5%RXe$7KT#xiN-m%c(4 zTqS(7m5MLdB-G!_1ICUQE-W$vH)Dm1x(*<{QfPWi*JVJraP|Gm*I!x-6!oZ!l{;#~XoDGquC|ke{SNt*>z# zczDHXCq1Eo>)IxI2?l#@nRe&wAYjQp?WcO;vUZ8~-`j&}gbmTwkDy`!deHMi4_yL4~wBErX@i#v1@c(g<}zibr{v`v>(YIR$jke>g1?ai zS+}mJnQpcmg>IL76A`-^AQ7{QuTgvi2jJcge;IJyQ|tkw$BW zk1Q+}ds$u+FKIpBaF{VA{pEO_K?X}+nP!mNg|u>*XAp)f9m>k-$&MAPIB{kD#HYMt<3-l&^g2k3%&df9|H2dk_O|oR_GtB8(mJG9FvCu+>8J5(A`fQ6W ziHsMTZ02m!Qa}ATo6Ve(X4BiO|1%Q{vij6y9edV~C5YB5ULWa3FnOROC>!x~gk-`3 g3M<*cj($y8^a`z{K^l>9SUK1(fwkseSAH${3(#>+pa1{> delta 2606 zcmX9=dt6QF8h*aD*1oN^*WRNXmyD)CJEA1rBbScj+QuN4Ar(1^O65MXRW8Y`S}{t< z6laPWCfAtU4bBKZU6ji(D3{8ZA(z6u^xJ$?J#LCiJyDbVQ8T;u4)ojaM^@+>f~znD*(B4Cvj z8#L7u$lA!vl@!2hGz*?giQjv`LP3FJQ(1Tmz27{IMR}9J>jK!?Y0kj$-`VB^p9G!4 zwiS{f1Abx0^LSwO0rpGZeC0al%ANmRQSm0r>kFLkJ+jK2&D_uxgmXu8;R9cRc{p)% zoelx(7jTiuQGja~&JrID^yuC=@vn2>fox$%2T1Dor z;!E`BD8~i-xcW*+cvS%uBn!zNdBA*&ke+RUBC2|bUmO-r*Y}_^E-C$0eq2+j z60h=ev};x+tq242Oj0H99}32TR2zJpf%In8R_`9buN_obf5(Bf&r=?$Oq|OAMONEd zd@9uAc2nuCyw$<6WTxlu)seHJXo3W&qbpni$F1rGsbnV4qw3^^%YncN>g*AZz=o!% z^KM@S7LHf{s1R?vS?a=`W?;-sb@4`Ppr*6>$S3(~534UuGXTDg>hc0B>e3SRoj~I6 zW~FXaM`0YcozqyQT8Q8!ji*=!eE3G=6XF1@=&1>)ruE7zn!wa}8Xp0gus*ZFoO3lv zI|l&$@-(YQbp-RCqS@$v7Z^TKliDtq1awf|t4*$tyK45XC+8rj7402b@%H&vtouuI zWF+y;sMDNr-vQRYNtvKAjW{t$>!p4{bVq8vUNi!0R%*xjO`y(Lqn%uu0obQ&7Y%Wy zrhcML$(;gR8>3Bgq%vRd)GDV8K3^RZ?Y7ccYwGekMmGu{fuh&C$sa|s?|EIE`5fhZLYMIN1DPvI3DcS!d6jN^(k0-` zYF$o0+Flo`JFsgsP4PXtE3P(R@;lv?_ryVVP!4HL7Ganu#ZbXfCRztjGkoD8T8EL6 zT(j7PJp{T`iKe7-8gPDM;M#pa!eMb{7UkY0Lkz1Q1=hD*44+Bid?$$!_oz9mGCw10 zS0OH+`V`FJ4>7B*1vs4}7WuuSlKP0n6DivQ8}XR*l1iFZDV{3x0pob_?22M?;92q7 zYAV^x2J!yXN5H9UvEh44tb44)0*a^xMUrj_dBrzY68Y=EGA~JdI3LWgQIZqr{*jZF zLY=MUWU}Nv#-8S0ljJw46d3zb8erWKcv2#nN2JhPt^SOxonA7RP(C}Hq_H3KiBFs~ zx>QtHVXN=uNkZunC}be6JpZsfQoY47`V;O#@{;)>b8fqd!WACz7u zFI8ol(U)~Bm8zFCg82-TYKCnFOy{I}E3)ddW~tteZUj@4-X$;y*yN!f*o%a_*-wcV zZGC>dte;ajm~_k1&l|oS%=Myv{w-f>nH%~glNk-AcKUVaQ|a6geZ~MP=b*v*jL;E4 z`9q~bG+AnL^v78ojrq*a$h>;$Pi`TL#fRw2>Xrlj2kWmzm}yp4>8~3bftY-K^{dWc zed6`EzPmx!-$DJ$HKgvUXjxl(h}5r>JIy5}_uY zXlsqGQ47GF%8l+9ZNYlF8+|*`^ftCPjy$}aEFSg2I699AA4@b&*g~iP$-gaO?%Bu={&scK#Y` diff --git a/src/translations/bitmessage_eo.ts b/src/translations/bitmessage_eo.ts index 9212f03b..2502c2d6 100644 --- a/src/translations/bitmessage_eo.ts +++ b/src/translations/bitmessage_eo.ts @@ -58,12 +58,12 @@ EmailGatewayRegistrationDialog - + Registration failed: Registrado malsukcesis: - + The requested email address is not available, please try a new one. Fill out the new desired email address (including @mailchuck.com) below: La dezirata retpoŝtadreso ne estas disponebla, bonvolu provi alian. Entajpu novan deziratan adreson (kune kun @mailchuck.com) sube: @@ -316,7 +316,7 @@ Bonvolu entajpi deziratan retpoŝtadreson (kune kun @mailchuck.com) sube:Ricevis konfirmon de la mesaĝo je %1 - + Broadcast queued. Elsendo en atendovico. @@ -542,7 +542,7 @@ Estas grava, ke vi faru sekurkopion de tiu dosiero. Ĉu vi volas malfermi la dos La mesaĝon kiun vi provis sendi estas tro longa je %1 bitokoj. (La maksimumo estas 261644 bitokoj.) Bonvolu mallongigi ĝin antaŭ sendado. - + Error: Your account wasn't registered at an email gateway. Sending registration now as %1, please wait for the registration to be processed before retrying sending. Eraro: Via konto ne estas registrita je retpoŝta kluzo. Registranta nun kiel %1, bonvolu atendi ĝis la registrado finos antaŭ vi reprovos sendi iun ajn. @@ -587,67 +587,67 @@ Estas grava, ke vi faru sekurkopion de tiu dosiero. Ĉu vi volas malfermi la dos - + Error: You must specify a From address. If you don't have one, go to the 'Your Identities' tab. Eraro: Vi devas elekti sendontan adreson. Se vi ne havas iun, iru al langeto 'Viaj identigoj'. - + Address version number Numero de adresversio - + Concerning the address %1, Bitmessage cannot understand address version numbers of %2. Perhaps upgrade Bitmessage to the latest version. Dum prilaborado de adreso adreso %1, Bitmesaĝo ne povas kompreni numerojn %2 de adresversioj. Eble ĝisdatigu Bitmesaĝon al la plej nova versio. - + Stream number Fluo numero - + Concerning the address %1, Bitmessage cannot handle stream numbers of %2. Perhaps upgrade Bitmessage to the latest version. Dum prilaborado de adreso %1, Bitmesaĝo ne povas priservi %2 fluojn numerojn. Eble ĝisdatigu Bitmesaĝon al la plej nova versio. - + Warning: You are currently not connected. Bitmessage will do the work necessary to send the message but it won't send until you connect. Atentu: Vi ne estas nun konektita. Bitmesaĝo faros necesan laboron por sendi mesaĝon, tamen ĝi ne sendos ĝin antaŭ vi konektos. - + Message queued. Mesaĝo envicigita. - + Your 'To' field is empty. Via "Ricevonto"-kampo malplenas. - + Right click one or more entries in your address book and select 'Send message to this address'. Dekstre alklaku kelka(j)n elemento(j)n en via adresaro kaj elektu 'Sendi mesaĝon al tiu adreso'. - + Fetched address from namecoin identity. Venigis adreson de namecoin-a identigo. - + New Message Nova mesaĝo - + From De - + Sending email gateway registration request Sendado de peto pri registrado je retpoŝta kluzo @@ -662,142 +662,142 @@ Estas grava, ke vi faru sekurkopion de tiu dosiero. Ĉu vi volas malfermi la dos La adreso kiun vi enmetis estas malĝusta. Ignoras ĝin. - + Error: You cannot add the same address to your address book twice. Try renaming the existing one if you want. Eraro: Vi ne povas duoble aldoni la saman adreson al via adresaro. Provu renomi la ekzistan se vi volas. - + Error: You cannot add the same address to your subscriptions twice. Perhaps rename the existing one if you want. Eraro: Vi ne povas aldoni duoble la saman adreson al viaj abonoj. Eble renomi la ekzistan se vi volas. - + Restart Restartigi - + You must restart Bitmessage for the port number change to take effect. Vi devas restartigi Bitmesaĝon por ke la ŝanĝo de la numero de pordo (Port Number) efektivigu. - + Bitmessage will use your proxy from now on but you may want to manually restart Bitmessage now to close existing connections (if any). Bitmesaĝo uzos vian prokurilon (proxy) ekde nun, sed eble vi volas permane restartigi Bitmesaĝon nun, por ke ĝi fermu eblajn ekzistajn konektojn. - + Number needed Numero bezonata - + Your maximum download and upload rate must be numbers. Ignoring what you typed. Maksimumaj elŝutrapido kaj alŝutrapido devas esti numeroj. Ignoras kion vi enmetis. - + Will not resend ever Resendos neniam - + Note that the time limit you entered is less than the amount of time Bitmessage waits for the first resend attempt therefore your messages will never be resent. Rigardu, ke la templimon vi enmetis estas pli malgrandan ol tempo dum kiu Bitmesaĝo atendas por resendi unuafoje, do viaj mesaĝoj estos senditaj neniam. - + Sending email gateway unregistration request Sendado de peto pri malregistrado de retpoŝta kluzo - + Sending email gateway status request Sendado de peto pri stato de retpoŝta kluzo - + Passphrase mismatch Pasfrazoj malsamas - + The passphrase you entered twice doesn't match. Try again. La pasfrazo kiun vi duoble enmetis malsamas. Provu denove. - + Choose a passphrase Elektu pasfrazon - + You really do need a passphrase. Vi ja vere bezonas pasfrazon. - + Address is gone Adreso foriris - + Bitmessage cannot find your address %1. Perhaps you removed it? Bitmesaĝo ne povas trovi vian adreson %1. Ĉu eble vi forviŝis ĝin? - + Address disabled Adreso malŝaltita - + Error: The address from which you are trying to send is disabled. You'll have to enable it on the 'Your Identities' tab before using it. Eraro: La adreso kun kiu vi provas sendi estas malŝaltita. Vi devos ĝin ŝalti en la langeto 'Viaj identigoj' antaŭ uzi ĝin. - + Entry added to the Address Book. Edit the label to your liking. Aldonis elementon al adresaro. Redaktu la etikedon laŭvole. - + Entry added to the blacklist. Edit the label to your liking. Aldonis elementon al la nigra listo. Redaktu la etikedon laŭvole. - + Error: You cannot add the same address to your blacklist twice. Try renaming the existing one if you want. Eraro: Vi ne povas duoble aldoni la saman adreson al via nigra listo. Provu renomi la jaman se vi volas. - + Moved items to trash. Movis elementojn al rubujo. - + Undeleted item. Malforviŝis elementon. - + Save As... Konservi kiel… - + Write error. Skriberaro. - + No addresses selected. Neniu adreso elektita. - + If you delete the subscription, messages that you already received will become inaccessible. Maybe you can consider disabling the subscription instead. Disabled subscriptions will not receive new messages, but you can still view messages you already received. Are you sure you want to delete the subscription? @@ -806,7 +806,7 @@ Are you sure you want to delete the subscription? Ĉu vi certe volas forviŝi la abonon? - + If you delete the channel, messages that you already received will become inaccessible. Maybe you can consider disabling the channel instead. Disabled channels will not receive new messages, but you can still view messages you already received. Are you sure you want to delete the channel? @@ -815,92 +815,92 @@ Are you sure you want to delete the channel? Ĉu vi certe volas forviŝi la kanalon? - + Do you really want to remove this avatar? Ĉu vi certe volas forviŝi tiun ĉi avataron? - + You have already set an avatar for this address. Do you really want to overwrite it? Vi jam agordis avataron por tiu ĉi adreso. Ĉu vi vere volas superskribi ĝin? - + Start-on-login not yet supported on your OS. Starto-dum-ensaluto ne estas ankoraŭ ebla en via operaciumo. - + Minimize-to-tray not yet supported on your OS. Plejetigo al taskopleto ne estas ankoraŭ ebla en via operaciumo. - + Tray notifications not yet supported on your OS. Taskopletaj sciigoj ne estas ankoraŭ eblaj en via operaciumo. - + Testing... Testado… - + This is a chan address. You cannot use it as a pseudo-mailing list. Tio ĉi estas kanaladreso. Vi ne povas ĝin uzi kiel kvazaŭ-dissendolisto. - + The address should start with ''BM-'' La adreso komencu kun "BM-" - + The address is not typed or copied correctly (the checksum failed). La adreso ne estis prave tajpita aŭ kopiita (kontrolsumo malsukcesis). - + The version number of this address is higher than this software can support. Please upgrade Bitmessage. La numero de adresversio estas pli alta ol tiun, kiun la programo poveblas subteni. Bonvolu ĝisdatigi Bitmesaĝon. - + The address contains invalid characters. La adreso enhavas malpermesitajn simbolojn. - + Some data encoded in the address is too short. Kelkaj datumoj koditaj en la adreso estas tro mallongaj. - + Some data encoded in the address is too long. Kelkaj datumoj koditaj en la adreso estas tro longaj. - + Some data encoded in the address is malformed. Kelkaj datumoj koditaj en la adreso estas misformitaj. - + Enter an address above. Enmetu adreson supre. - + Address is an old type. We cannot display its past broadcasts. Malnova speco de adreso. Ne povas montri ĝiajn antaŭajn elsendojn. - + There are no recent broadcasts from this address to display. Neniaj lastatempaj elsendoj de tiu ĉi adreso por montri. - + You are using TCP port %1. (This can be changed in the settings). Vi uzas TCP-pordon %1 (tio ĉi estas ŝanĝebla en la agordoj). @@ -1110,7 +1110,7 @@ Are you sure you want to delete the channel? Aldoni novan elementon - + Display the %1 recent broadcast(s) from this address. Montri la %1 lasta(j)n elsendo(j)n de tiu adreso. @@ -1120,37 +1120,37 @@ Are you sure you want to delete the channel? La nova versio de PyBitmessage estas disponebla: %1. Elŝutu ĝin de https://github.com/Bitmessage/PyBitmessage/releases/latest - + Waiting for PoW to finish... %1% Atendado ĝis laborpruvo finiĝos… %1% - + Shutting down Pybitmessage... %1% Fermado de PyBitmessage… %1% - + Waiting for objects to be sent... %1% Atendado ĝis objektoj estos senditaj… %1% - + Saving settings... %1% Konservado de agordoj… %1% - + Shutting down core... %1% Fermado de kerno… %1% - + Stopping notifications... %1% Haltigado de sciigoj… %1% - + Shutdown imminent... %1% Fermado tuj… %1% @@ -1165,7 +1165,7 @@ Are you sure you want to delete the channel? %n tago%n tagoj - + Shutting down PyBitmessage... %1% Fermado de PyBitmessage… %1% @@ -1215,61 +1215,61 @@ Are you sure you want to delete the channel? Atentu: Via disko aŭ subdisko estas plenplena. Bitmesaĝo fermiĝos. - + Error! Could not find sender address (your address) in the keys.dat file. Eraro! Ne povas trovi adreson de sendanto (vian adreson) en la dosiero keys.dat. - + Doing work necessary to send broadcast... Kalkulado de laborpruvo, kiu endas por sendi elsendon… - + Broadcast sent on %1 Elsendo sendita je %1 - + Encryption key was requested earlier. Peto pri ĉifroŝlosilo jam sendita. - + Sending a request for the recipient's encryption key. Sendado de peto pri ĉifroŝlosilo de ricevonto. - + Looking up the receiver's public key Serĉado de publika ĉifroŝlosilo de ricevonto - + Problem: Destination is a mobile device who requests that the destination be included in the message but this is disallowed in your settings. %1 Eraro: celadreso estas portebla aparato kiu necesas, ke la celadreso estu enhavita en la mesaĝo, sed tio estas malpermesita ne viaj agordoj. %1 - + Doing work necessary to send message. There is no required difficulty for version 2 addresses like this. Kalkulado de laborpruvo, kiu endas por sendi mesaĝon. Malfacilaĵo ne estas bezonata por adresoj versioj 2, kiel tiu ĉi adreso. - + Doing work necessary to send message. Receiver's required difficulty: %1 and %2 Kalkulado de laborpruvo, kiu endas por sendi mesaĝon. Ricevonto postulas malfacilaĵon: %1 kaj %2 - + Problem: The work demanded by the recipient (%1 and %2) is more difficult than you are willing to do. %3 Eraro: la demandita laboro de la ricevonto (%1 kaj %2) estas pli malfacila ol vi pretas fari. %3 - + Problem: You are trying to send a message to yourself or a chan but your encryption key could not be found in the keys.dat file. Could not encrypt message. %1 Eraro: Vi provis sendi mesaĝon al vi mem aŭ al kanalo, tamen via ĉifroŝlosilo ne estas trovebla en la dosiero keys.dat. Mesaĝo ne povis esti ĉifrita. %1 @@ -1279,7 +1279,7 @@ Ricevonto postulas malfacilaĵon: %1 kaj %2 Kalkulado de laborpruvo, kiu endas por sendi mesaĝon. - + Message sent. Waiting for acknowledgement. Sent on %1 Mesaĝo sendita. Atendado je konfirmo. Sendita je %1 @@ -1289,22 +1289,22 @@ Ricevonto postulas malfacilaĵon: %1 kaj %2 Kalkulado de laborpruvo, kiu endas por peti pri ĉifroŝlosilo. - + Broadcasting the public key request. This program will auto-retry if they are offline. Elsendado de peto pri publika ĉifroŝlosilo. La programo reprovos se ili estas eksterrete. - + Sending public key request. Waiting for reply. Requested at %1 Sendado de peto pri publika ĉifroŝlosilo. Atendado je respondo. Petis je %1 - + UPnP port mapping established on port %1 UPnP pord-mapigo farita je pordo %1 - + UPnP port mapping removed UPnP pord-mapigo forigita @@ -1314,7 +1314,7 @@ Ricevonto postulas malfacilaĵon: %1 kaj %2 Marki ĉiujn mesaĝojn kiel legitajn - + Are you sure you would like to mark all messages read? Ĉu vi certe volas marki ĉiujn mesaĝojn kiel legitajn? @@ -1324,22 +1324,22 @@ Ricevonto postulas malfacilaĵon: %1 kaj %2 Kalkulado de laborpruvo, kiu endas por sendi elsendon. - + Proof of work pending Laborpruvo haltigita - + %n object(s) pending proof of work Haltigis laborpruvon por %n objektoHaltigis laborpruvon por %n objektoj - + %n object(s) waiting to be distributed %n objekto atendas je sendato%n objektoj atendas je sendato - + Wait until these tasks finish? Ĉu atendi ĝis tiujn taskojn finos? @@ -1399,7 +1399,7 @@ Ricevonto postulas malfacilaĵon: %1 kaj %2 Ne povis kompreni NMControl. - + Your GPU(s) did not calculate correctly, disabling OpenCL. Please report to the developers. Via(j) vidprocesoro(j) ne kalkulis senerare, malaktiviganta OpenCL. Bonvolu raporti tion al programistoj. @@ -1428,72 +1428,77 @@ Bonvenon al facila kaj sekura Bitmesaĝo Ĉu problemo kun konektado? Provu aktivigi UPnP en retaj agordoj. - + + You are trying to send an email instead of a bitmessage. This requires registering with a gateway. Attempt to register? + Vi provas sendi retmesaĝon anstataŭ bitmesaĝ-mesaĝon. Tio ĉi postulas registri ĉe retpoŝta kluzo. Ĉu provi registri? + + + Error: Bitmessage addresses start with BM- Please check the recipient address %1 Eraro: bitmesaĝaj adresoj komenciĝas kun BM-. Bonvolu kontroli la adreson de ricevonto %1 - + Error: The recipient address %1 is not typed or copied correctly. Please check it. Eraro: la adreso de ricevonto %1 estas malprave tajpita aŭ kopiita. Bonvolu kontroli ĝin. - + Error: The recipient address %1 contains invalid characters. Please check it. Eraro: la adreso de ricevonto %1 enhavas malpermesatajn simbolojn. Bonvolu kontroli ĝin. - + Error: The version of the recipient address %1 is too high. Either you need to upgrade your Bitmessage software or your acquaintance is being clever. Eraro: la versio de adreso de ricevonto %1 estas tro alta. Eble vi devas ĝisdatigi vian bitmesaĝan programon aŭ via sagaca konato uzas alian programon. - + Error: Some data encoded in the recipient address %1 is too short. There might be something wrong with the software of your acquaintance. Eraro: kelkaj datumoj koditaj en la adreso de ricevonto %1 estas tro mallongaj. Povus esti ke io en la programo de via konato malfunkcias. - + Error: Some data encoded in the recipient address %1 is too long. There might be something wrong with the software of your acquaintance. Eraro: kelkaj datumoj koditaj en la adreso de ricevonto %1 estas tro longaj. Povus esti ke io en la programo de via konato malfunkcias. - + Error: Some data encoded in the recipient address %1 is malformed. There might be something wrong with the software of your acquaintance. Eraro: kelkaj datumoj koditaj en la adreso de ricevonto %1 estas misformitaj. Povus esti ke io en la programo de via konato malfunkcias. - + Error: Something is wrong with the recipient address %1. Eraro: io malĝustas kun la adreso de ricevonto %1. - + Synchronisation pending Samtempigado haltigita - + Bitmessage hasn't synchronised with the network, %n object(s) to be downloaded. If you quit now, it may cause delivery delays. Wait until the synchronisation finishes? Bitmesaĝo ne estas samtempigita kun la reto, %n objekto elŝutendas. Se vi eliros nun, tio povas igi malfruiĝojn de liveradoj. Ĉu atendi ĝis la samtempigado finiĝos?Bitmesaĝo ne estas samtempigita kun la reto, %n objektoj elŝutendas. Se vi eliros nun, tio povas igi malfruiĝojn de liveradoj. Ĉu atendi ĝis la samtempigado finiĝos? - + Not connected Nekonektita - + Bitmessage isn't connected to the network. If you quit now, it may cause delivery delays. Wait until connected and the synchronisation finishes? Bitmesaĝo ne estas konektita al la reto. Se vi eliros nun, tio povas igi malfruiĝojn de liveradoj. Ĉu atendi ĝis ĝi konektos kaj la samtempigado finiĝos? - + Waiting for network connection... Atendado je retkonekto… - + Waiting for finishing synchronisation... Atendado ĝis samtempigado finiĝos… @@ -1633,7 +1638,7 @@ La ‘hazardnombra’ adreso estas antaŭagordita, sed antaŭkalkuleblaj adresoj (saves you some bandwidth and processing power) - (konservas iomete rettrafikon kaj komputopovon) + (konservas iomete da ret-trafiko kaj komput-povo) @@ -2084,17 +2089,17 @@ La ‘hazardnombra’ adreso estas antaŭagordita, sed antaŭkalkuleblaj adresoj proofofwork - + C PoW module built successfully. C PoW modulo konstruita sukcese. - + Failed to build C PoW module. Please build it manually. Malsukcesis konstrui C PoW modulon. Bonvolu konstrui ĝin permane. - + C PoW module unavailable. Please build it. C PoW modulo nedisponebla. Bonvolu konstrui ĝin. From 022e0ce59327eec009f92023660d65ccd9a744f4 Mon Sep 17 00:00:00 2001 From: PyBitmessage Translations Date: Sat, 26 Aug 2017 04:35:44 +0200 Subject: [PATCH 199/407] Auto-updated language ja from transifex --- src/translations/bitmessage_ja.qm | Bin 66390 -> 66676 bytes src/translations/bitmessage_ja.ts | 229 +++++++++++++++--------------- 2 files changed, 117 insertions(+), 112 deletions(-) diff --git a/src/translations/bitmessage_ja.qm b/src/translations/bitmessage_ja.qm index 37e8086d9a717d80de0d10fe159843685ad6c397..4cf32fd716f80eb8c6aeb03193bef46811d9b2b8 100644 GIT binary patch delta 2732 zcmXw5c~}$o7Cp&iotY$6(buBHxR6?_IPytZ6hsAg;|@Yai4q_zK_M1b0#X+c1RSgt zaVv<$ne0JPd5yW|Rt9tv4Q7%-+MWX%FFc2)--H~@KUHBhxY zZ-vqEWCY}gGl1vGP<+GC*Iz&x6iE0C>cAtwv}S0(ssn`g&?)dGuyYPNm2UuQ0^z)5 zJ+QbI`l1|Q-Ff&yW-9}+mt`eI!)gBcu$p~`;vZ^E- z%TEIz{2|#=O2s|*5UZqq(tNY{Mrw3EI$ApPZW=JTN;)Q$a7vgo+W!?vkSF>wddCHa zG;3fbS^iF%y@1YM6(HRnejn%&BQ2S20|L)WE3{?6*Q=$q;e_EYq}Sv70@K5!Kdq=C zMPsEmwPxV^`_hN9Bw*JKMz-7oI5mUev*f^wnT+Q0en6ka=vI*=C7w)|{f)rVfuI#Q705K14r!zQnoTeeK}xsW;}R0;K!HDCkZJemYnf587SdZX2jeEAY%$M zZXt=eLdit8ks99l%o4Y|K(~v`=8F|T?iprt+Z-UaoY^*K6%lr2j`F0^Zv&Zgo9Nke zo2gS;96+z6_8(;Cf0>JGO@LP{(^xQ$T!>(rf=PjxNapEcBCuA={Bd|H5E>=ZxvU3H zddPHhsb?OnmUY_`LZ%Ow`5p=Y?x)B`nZ5zG*UHA$wvd7j*}N|KRN!ryWv?5J#7S8! zR|b^cmBsbzN`A>~GW!CO>S&`Z`OPSxP$xc-`LVSTvhSNeqI@Ss54oT8$Odtd+^>H_ zr93^A*f!SYw|C>DSG2XdBIEa;Yy)+OKuE(pp!R^nG6^<$y=FX zs(Oa}bwDS;IZe?ihQgjOis`$k8|pF?;fbVTrHdjy(grM7DiW@_lQ=yTi*ku$(`m)p z&qxISfr`TM4}e#P#W025QKVKJYS*r`P;oX)3k0=XtA?LAfh{qGw9WU@O(kDATtO2i&G9S5N$a zCZR>S-s3jl>8s4`M$K|`yE6BI3F2RhUhZY}%5Sq8Y1T(}V2|%RaF@CR%Sx1oCQxY! z$;xUE!e;}-NlN4RY$uhM;ssR}tnzyC6o@ERP4}Bg)N55CC&(_BzN+LvHyYL>s?4HL zawSxi*Pm?7->wo*X@T{_RL5tH0FHTzRZ2hST_aVkD;EM^RjZvIP6v*ssk`qcVVhX7 ztIF8>s9Ze}?|{!&sYBkez&Q_fl8L%;bfh}%&$qyd8)BHs*uO@t-kSa+katGCV;F^( zeWEVkN%oCBsIGN)2F?#r*S?_|&Si_GDx>4Hp5+#j=_p~lOiBSRw6a|+B&XEE_LkfS zx_`(T)2{;MZER4+cQm{vHll#IJ3nVF%@fIV_ccsZIg9eqn%K98IFj=Xsk#?4ZUa z)-6pZG;_HpZvt^`TtWRPS~gcaxkB|2>bnEnw{JF(c`e-8R4cH0GI#dZG9d8>?x)Qr z;HneXu;eMwoX_1HyO9WjYwk?_T7I5uHpI{}I>-w_L^$R???0ejxWjxvB|{geD?e*R zENxFGKH}9}8lQc<<%?}JmB;zm(vfuV9d~}gxUF=L-Q?{*52c-*!!HSu(7dhZ*Zq)7 zgf{W{!^!9ug?#?J@w7w)v6eMDiW2!_5*pX2(hhvA=1+b>h0cD)*F8$1IjQC^#Srh3 za{jW8c+a@ZH@u?j@n?s3#0TPiet=hpx|uV{>$ zDZi<=W}vtS*nM0hWF8~qQ#9VKCm<$rA31I}YhqjJtaYz6wgWaGJ5rP22&N0=Z_UzC zIW+r5&GO}b)cG}d^KXNzdzVY5RAr112rj4lr#2haS(gV_P$U`g5WyZ**)Json9Z z7yTqhHB z_niO^#_7TfJ_kN_)!A~0$Op4^3kOgkRqu3Zqbb4ZNxGbyA~6LkI8i7F{%M%Ew;RkguJ^xBG;Om72AQtWggOGpqT!hVW8E0hSkg<>K0vt^kP7c~*P8B`L`qWpOjih_)sf5-kfCSraYoKccJ@nAn~p*=mob;3%`* el5Ae$W*A|&TjCek+jIXvOiT;yTH%@7GwFX!=WGuE delta 2539 zcmX9=d0bBU9)8Yy&byuSE<>)lDrM_6Qb{w3jI5FEMJZ*9L?o4^##+54l4x_RSt1QL z)0>izCDNG5QnxITQDc;BsZ8l|$8x{!``7b1=lss^_k5q{`F_voOyX)2x%3{&;Q)pL zmB%xB>s-qcfFEpu4SxV36M(nA;Hqtai6g<)1pt$*!L>L79(4Z;79E0g*;U};;*1#G zxR$|?p4$qvEr2YSrMKfC_i-nzgVMbO@N9u5y9#jp3fR=5MWIt1qHQh~LV z8LzDjPdzd8S6bz}2TmD>fcWp>QcJv!I%DM5t-$#Iz&&RZpijc2-M;|lekRNdz~tv% zz`%A)-_=fBJurPYPq+#*Le~S2BjLIBB2~NEggGhjNe%^`?Zup>^!(W;_!hgt3^r2C zjdZ0lO5ra{CQKC%GQ$i{_F;>6HnESx-nCZ1=}a71+8qc?LC!fd+P^>YQf31ukHa{M zo)-qA_%799Cr6ETJ7D|^b*t_G^X8)7g3hgcfX4CX0YwhFu2Ef4zKrs4CouRV(>+TA zT$;*Qn{&XdF-+efB`}~qi1|j$jBB@iAm|D+ z*53t){EN}o(gwDTOrReTKW@eZfj0JzV?sXA`xmuL#0V02NjS4Lzya8HoY_@Kg7$jH z>^G7iZPv_%9I|ndnz{6Cu2>^+l8949DXTM-I7&u7jt6F3l1z#toEabq8S@r+zCjXZ zUrf<4NWzmM0NrefA!a$y&qI=2+YPw6RI>g5&p%62Rt+e8#_whhxRLF$Ih<2IUDlfkT{6z?vz%*?)xwgSXRg$nzxxa z+OX$Uq~5cy*&BQ4THnA{$%BEuktRI(NDSw!42kd9cjsmSbC+=1-l;&THK(0VA!}N} z^*rGRQeVh`e+Ve@oAmwG5&&a>Y?nH_5qZ zF-GcU`yfl25J!Yxtdk}cOaiWCOH*9wr0{#vy(4J#&SBEb4${0PQG6=ZNgCgaYMGVc z)oYp8Ph@>%q%3e1h3T|h7QQ$Fh?dKi-*ck!EoDEZ6TcVvvZU3d>==95fhkXc&TLuE zqkmJEL-sF`>h5JHGY-}R-}}poQq6!P9kR1u35pC`vRT}!5U*%}6rJLt?^xh`U-6pU%{<3W(Y0|UsgbAb(d-3WSf;c%PHI2z z6niUl{m!*1r{Ob@uw3c)nFp?0DBvX zxv`J3@W>3B;%AhVPUgT3D`n+Js^R(?u~?xqlq+~)B^i!myxH_v>e|P=SuiOn3FiAT zO@PHfUYBqeC}`$=w&qcn>iI>P#NE7|4{n)8)}-^6U;$`vT<#+SzxQ3A90>O`_^%18cB zf67jp1OIvt5o>rXFw^tNgNcH29c9HnMBv#Pz~4&Xo1&<$bU_{OPIXm?Mx~V@b&)V) zrVY)#bivKH6d2W07;Q!g$Zr<(Q>X>AwwbWnQ_!C$KC3f?S)a+9=1IcB4dh8#zOZe5 z0^w94{qjQ~?1PYbhgz+suW&&59VPCx@bkwVqA4x5frdsF6P^+b?wp~l7bK_MzN0XiHiK?AJQ)qOUi}!e)A=_Vd zfuX)#Vl?544%Ow|RE~d}s_Gf3=x|k49jd2UxnEVIC3drGRCRBA(?qIN{l4K2eFoZ9 zom)uVg<7?uxtP>nskR8C7-Sw)4_H_ZSl?Fb_R;?OerkL11f}Mf+A;Y8*&CuB(Nzj@ zy)ej7U#ni$MQ0`dp^i8m0c@YCUT&B}ACAA(>&K^&b2@c&v>WBys6OtHH> diff --git a/src/translations/bitmessage_ja.ts b/src/translations/bitmessage_ja.ts index 9733c02a..ed8704ce 100644 --- a/src/translations/bitmessage_ja.ts +++ b/src/translations/bitmessage_ja.ts @@ -58,12 +58,12 @@ EmailGatewayRegistrationDialog - + Registration failed: 登録に失敗しました: - + The requested email address is not available, please try a new one. Fill out the new desired email address (including @mailchuck.com) below: リクエストしたメールアドレスは利用できません。新しいメールアドレスをお試しください。 新しい希望メールアドレス (@mailchuck.com を含む) を次のように記入してください: @@ -318,7 +318,7 @@ Please type the desired email address (including @mailchuck.com) below: メッセージの確認を受け取りました %1 - + Broadcast queued. 配信がキューに入りました。 @@ -543,7 +543,7 @@ It is important that you back up this file. Would you like to open the file now? 送信しようとしているメッセージが %1 バイト長すぎます。 (最大は261644バイトです)。 送信する前に短くしてください。 - + Error: Your account wasn't registered at an email gateway. Sending registration now as %1, please wait for the registration to be processed before retrying sending. エラー: アカウントがメールゲートウェイに登録されていません。 今 %1 として登録を送信しています。送信を再試行する前に、登録が処理されるまでお待ちください。 @@ -588,67 +588,67 @@ It is important that you back up this file. Would you like to open the file now? - + Error: You must specify a From address. If you don't have one, go to the 'Your Identities' tab. エラー: 送信元アドレスを指定してください。まだ作成していない場合には「アドレス一覧」のタブを開いてください。 - + Address version number アドレスのバージョン番号 - + Concerning the address %1, Bitmessage cannot understand address version numbers of %2. Perhaps upgrade Bitmessage to the latest version. アドレス %1 に接続。%2 のバージョン番号は処理できません。Bitmessageを最新のバージョンへアップデートしてください。 - + Stream number ストリーム番号 - + Concerning the address %1, Bitmessage cannot handle stream numbers of %2. Perhaps upgrade Bitmessage to the latest version. アドレス %1 に接続。%2 のストリーム番号は処理できません。Bitmessageを最新のバージョンへアップデートしてください。 - + Warning: You are currently not connected. Bitmessage will do the work necessary to send the message but it won't send until you connect. 警告: 接続されていません。Bitmessageはメッセージの処理を行いますが、ネットワークに接続するまで送信はされません。 - + Message queued. メッセージがキューに入りました。 - + Your 'To' field is empty. 宛先が指定されていません。 - + Right click one or more entries in your address book and select 'Send message to this address'. アドレス帳から一つ、または複数のアドレスを右クリックして「このアドレスへ送信」を選んでください。 - + Fetched address from namecoin identity. namecoin IDからアドレスを取得。 - + New Message 新規メッセージ - + From 送信元 - + Sending email gateway registration request メールゲートウェイの登録リクエストを送信しています @@ -663,142 +663,142 @@ It is important that you back up this file. Would you like to open the file now? 入力されたアドレスは不正です。無視されました。 - + Error: You cannot add the same address to your address book twice. Try renaming the existing one if you want. エラー: 同じアドレスを複数アドレス帳に追加する事はできません。既存の項目をリネームしてください。 - + Error: You cannot add the same address to your subscriptions twice. Perhaps rename the existing one if you want. エラー: 購読に、同じアドレスを2回追加することはできません。 必要に応じて、既存の名前を変更してください。 - + Restart 再開 - + You must restart Bitmessage for the port number change to take effect. ポート番号の変更を有効にするにはBitmessageを再起動してください。 - + Bitmessage will use your proxy from now on but you may want to manually restart Bitmessage now to close existing connections (if any). プロキシの設定を有効にするには手動でBitmessageを再起動してください。既に接続がある場合は切断されます。 - + Number needed 数字が必要です - + Your maximum download and upload rate must be numbers. Ignoring what you typed. 最大ダウンロード数とアップロード数は数字にする必要があります。 入力されたものを無視します。 - + Will not resend ever 今後再送信されません - + Note that the time limit you entered is less than the amount of time Bitmessage waits for the first resend attempt therefore your messages will never be resent. 入力した時間制限は、Bitmessageが最初の再送試行を待つ時間よりも短いため、メッセージは再送信されないことにご注意ください。 - + Sending email gateway unregistration request メールゲートウェイの登録抹消リクエストを送信しています - + Sending email gateway status request メールゲートウェイの状態リクエストを送信しています - + Passphrase mismatch パスフレーズが一致しません - + The passphrase you entered twice doesn't match. Try again. 再度入力されたパスフレーズが一致しません。再入力してください。 - + Choose a passphrase パスフレーズを選択してください - + You really do need a passphrase. パスフレーズが必要です。 - + Address is gone アドレスが無効になりました - + Bitmessage cannot find your address %1. Perhaps you removed it? アドレス %1 が見つかりません。既に削除していませんか? - + Address disabled アドレスが無効になりました - + Error: The address from which you are trying to send is disabled. You'll have to enable it on the 'Your Identities' tab before using it. エラー: 送信しようとしたアドレスは無効になっています。使用する前に「アドレス一覧」で有効にしてください。 - + Entry added to the Address Book. Edit the label to your liking. アドレス帳に項目が追加されました。ラベルは自由に編集できます。 - + Entry added to the blacklist. Edit the label to your liking. ブラックリストに項目が追加されました。ラベルは自由に編集できます。 - + Error: You cannot add the same address to your blacklist twice. Try renaming the existing one if you want. エラー: ブラックリストに同じアドレスを2回追加することはできません。 必要に応じて既存の名前を変更してみてください。 - + Moved items to trash. アイテムをゴミ箱へ移動。 - + Undeleted item. アイテムの削除を元に戻します。 - + Save As... 形式を選択して保存 - + Write error. 書き込みエラー。 - + No addresses selected. アドレスが未選択です。 - + If you delete the subscription, messages that you already received will become inaccessible. Maybe you can consider disabling the subscription instead. Disabled subscriptions will not receive new messages, but you can still view messages you already received. Are you sure you want to delete the subscription? @@ -807,7 +807,7 @@ Are you sure you want to delete the subscription? 購読を削除してもよろしいですか? - + If you delete the channel, messages that you already received will become inaccessible. Maybe you can consider disabling the channel instead. Disabled channels will not receive new messages, but you can still view messages you already received. Are you sure you want to delete the channel? @@ -816,92 +816,92 @@ Are you sure you want to delete the channel? チャンネルを削除してもよろしいですか? - + Do you really want to remove this avatar? このアバターを削除してもよろしいですか? - + You have already set an avatar for this address. Do you really want to overwrite it? すでにこのアドレスのアバターを設定しています。 上書きしてもよろしいですか? - + Start-on-login not yet supported on your OS. ログイン時に開始は、まだお使いのOSでサポートされていません。 - + Minimize-to-tray not yet supported on your OS. トレイに最小化は、まだお使いのOSでサポートされていません。 - + Tray notifications not yet supported on your OS. トレイ通知は、まだお使いのOSでサポートされていません。 - + Testing... テスト中 - + This is a chan address. You cannot use it as a pseudo-mailing list. chanアドレスは仮想メーリングリストのアドレスには使用できません。 - + The address should start with ''BM-'' アドレスは「BM-」から始まります - + The address is not typed or copied correctly (the checksum failed). このアドレスは正しく入力、またはコピーされていません。(チェックサムが一致しません)。 - + The version number of this address is higher than this software can support. Please upgrade Bitmessage. このアドレスのバージョン番号はこのプログラムのサポート範囲外です。Bitmessageをアップデートしてください。 - + The address contains invalid characters. 入力されたアドレスは不正な文字を含んでいます。 - + Some data encoded in the address is too short. このアドレスでエンコードされたデータが短すぎます。 - + Some data encoded in the address is too long. このアドレスでエンコードされたデータが長過ぎます。 - + Some data encoded in the address is malformed. このアドレスでエンコードされた一部のデータが不正です。 - + Enter an address above. 上にアドレスを入力してください。 - + Address is an old type. We cannot display its past broadcasts. アドレスが古い形式です。 過去の配信は表示できません。 - + There are no recent broadcasts from this address to display. このアドレスから表示する最近の配信はありません。 - + You are using TCP port %1. (This can be changed in the settings). 使用中のポート %1 (設定で変更できます)。 @@ -1111,7 +1111,7 @@ Are you sure you want to delete the channel? 新しい項目を追加 - + Display the %1 recent broadcast(s) from this address. このアドレスから%1の最新の配信を表示します。 @@ -1121,37 +1121,37 @@ Are you sure you want to delete the channel? 新しいバージョンの PyBitmessage が利用可能です: %1。 https://github.com/Bitmessage/PyBitmessage/releases/latest からダウンロードしてください - + Waiting for PoW to finish... %1% PoW(証明)が完了するのを待っています... %1% - + Shutting down Pybitmessage... %1% Pybitmessageをシャットダウンしています... %1% - + Waiting for objects to be sent... %1% オブジェクトの送信待ち... %1% - + Saving settings... %1% 設定を保存しています... %1% - + Shutting down core... %1% コアをシャットダウンしています... %1% - + Stopping notifications... %1% 通知を停止しています... %1% - + Shutdown imminent... %1% すぐにシャットダウンします... %1% @@ -1166,7 +1166,7 @@ Are you sure you want to delete the channel? %n 日 - + Shutting down PyBitmessage... %1% PyBitmessageをシャットダウンしています... %1% @@ -1216,61 +1216,61 @@ Are you sure you want to delete the channel? アラート: ディスクまたはデータストレージのボリュームがいっぱいです。 Bitmessageが終了します。 - + Error! Could not find sender address (your address) in the keys.dat file. エラー! keys.datファイルで送信元アドレス (あなたのアドレス) を見つけることができませんでした。 - + Doing work necessary to send broadcast... 配信に必要な処理を行っています... - + Broadcast sent on %1 配信が送信されました %1 - + Encryption key was requested earlier. 暗号鍵は以前にリクエストされました。 - + Sending a request for the recipient's encryption key. 受信者の暗号鍵のリクエストを送信します。 - + Looking up the receiver's public key 受信者の公開鍵を探しています - + Problem: Destination is a mobile device who requests that the destination be included in the message but this is disallowed in your settings. %1 問題: メッセージに含まれた宛先のリクエストはモバイルデバイスですが、設定では許可されていません。 %1 - + Doing work necessary to send message. There is no required difficulty for version 2 addresses like this. メッセージの送信に必要な処理を行っています。 このようなバージョン2のアドレスには、必要な難易度はありません。 - + Doing work necessary to send message. Receiver's required difficulty: %1 and %2 メッセージの送信に必要な処理を行っています。 受信者の必要な難易度: %1 および %2 - + Problem: The work demanded by the recipient (%1 and %2) is more difficult than you are willing to do. %3 問題: 受信者が要求している処理 (%1 および %2) は、現在あなたが設定しているよりも高い難易度です。 %3 - + Problem: You are trying to send a message to yourself or a chan but your encryption key could not be found in the keys.dat file. Could not encrypt message. %1 問題: あなた自身またはチャンネルにメッセージを送信しようとしていますが、暗号鍵がkeys.datファイルに見つかりませんでした。 メッセージを暗号化できませんでした。 %1 @@ -1280,7 +1280,7 @@ Receiver's required difficulty: %1 and %2 メッセージの送信に必要な処理を行っています。 - + Message sent. Waiting for acknowledgement. Sent on %1 メッセージを送信しました。 確認応答を待っています。 %1 で送信しました @@ -1290,22 +1290,22 @@ Receiver's required difficulty: %1 and %2 暗号鍵のリクエストに必要な処理を行っています。 - + Broadcasting the public key request. This program will auto-retry if they are offline. 公開鍵のリクエストを配信しています。 このプログラムがオフラインの場合、自動的に再試行されます。 - + Sending public key request. Waiting for reply. Requested at %1 公開鍵のリクエストを送信しています。 返信を待っています。 %1 でリクエストしました - + UPnP port mapping established on port %1 ポート%1でUPnPポートマッピングが確立しました - + UPnP port mapping removed UPnPポートマッピングを削除しました @@ -1315,7 +1315,7 @@ Receiver's required difficulty: %1 and %2 すべてのメッセージを既読にする - + Are you sure you would like to mark all messages read? すべてのメッセージを既読にしてもよろしいですか? @@ -1325,22 +1325,22 @@ Receiver's required difficulty: %1 and %2 配信に必要な処理を行っています。 - + Proof of work pending PoW(証明)を待っています - + %n object(s) pending proof of work %n オブジェクトが証明待ち (PoW) - + %n object(s) waiting to be distributed %n オブジェクトが配布待ち - + Wait until these tasks finish? これらのタスクが完了するまで待ちますか? @@ -1400,7 +1400,7 @@ Receiver's required difficulty: %1 and %2 NMControl を理解できませんでした。 - + Your GPU(s) did not calculate correctly, disabling OpenCL. Please report to the developers. GPUが正しく求められないため、OpenCLが無効になりました。 開発者に報告してください。 @@ -1430,72 +1430,77 @@ Receiver's required difficulty: %1 and %2 接続に問題がありますか? ネットワーク設定でUPnPを有効にしてみてください - + + You are trying to send an email instead of a bitmessage. This requires registering with a gateway. Attempt to register? + Bitmessage の代わりにメールを送信しようとしています。 これは、ゲートウェイに登録する必要があります。 登録しますか? + + + Error: Bitmessage addresses start with BM- Please check the recipient address %1 エラー: BitmessageのアドレスはBM-で始まります。 受信者のアドレス %1 を確認してください - + Error: The recipient address %1 is not typed or copied correctly. Please check it. エラー: 受信者のアドレス %1 は正しく入力、またはコピーされていません。確認して下さい。 - + Error: The recipient address %1 contains invalid characters. Please check it. エラー: 受信者のアドレス %1 は不正な文字を含んでいます。確認して下さい。 - + Error: The version of the recipient address %1 is too high. Either you need to upgrade your Bitmessage software or your acquaintance is being clever. エラー: 受信者アドレスのバージョン %1 は高すぎます。 Bitmessageソフトウェアをアップグレードする必要があるか、連絡先が賢明になっているかのいずれかです。 - + Error: Some data encoded in the recipient address %1 is too short. There might be something wrong with the software of your acquaintance. エラー: アドレス %1 でエンコードされたデータが短すぎます。連絡先のソフトウェアが何かしら誤っている可能性があります。 - + Error: Some data encoded in the recipient address %1 is too long. There might be something wrong with the software of your acquaintance. エラー: 受信者のアドレス %1 でエンコードされたデータが短すぎます。連絡先のソフトウェアが何かしら誤っている可能性があります。 - + Error: Some data encoded in the recipient address %1 is malformed. There might be something wrong with the software of your acquaintance. エラー: 受信者のアドレス %1 でエンコードされたデータの一部が不正です。連絡先のソフトウェアが何かしら誤っている可能性があります。 - + Error: Something is wrong with the recipient address %1. エラー: 受信者のアドレス %1 には何かしら誤りがあります。 - + Synchronisation pending 同期を保留しています - + Bitmessage hasn't synchronised with the network, %n object(s) to be downloaded. If you quit now, it may cause delivery delays. Wait until the synchronisation finishes? Bitmessageはネットワークと同期していません。%n のオブジェクトをダウンロードする必要があります。 今、終了すると、配送が遅れることがあります。 同期が完了するまで待ちますか? - + Not connected 未接続 - + Bitmessage isn't connected to the network. If you quit now, it may cause delivery delays. Wait until connected and the synchronisation finishes? Bitmessageはネットワークに接続していません。 今、終了すると、配送が遅れることがあります。 接続して、同期が完了するまで待ちますか? - + Waiting for network connection... ネットワーク接続を待っています... - + Waiting for finishing synchronisation... 同期の完了を待っています... @@ -2085,17 +2090,17 @@ The 'Random Number' option is selected by default but deterministic ad proofofwork - + C PoW module built successfully. C PoW モジュールのビルドに成功しました。 - + Failed to build C PoW module. Please build it manually. C PoW モジュールのビルドに失敗しました。手動でビルドしてください。 - + C PoW module unavailable. Please build it. C PoW モジュールが利用できません。ビルドしてください。 From fc960cbf86d6f948e04d58ee10ed6f1c73233be2 Mon Sep 17 00:00:00 2001 From: Dmitri Bogomolov <4glitch@gmail.com> Date: Tue, 29 Aug 2017 13:50:49 +0300 Subject: [PATCH 200/407] Fixed own logical error when missing msgpack package is being appended to install_requires list instead of the available one. --- setup.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/setup.py b/setup.py index 0da5680a..9357284b 100644 --- a/setup.py +++ b/setup.py @@ -230,9 +230,9 @@ if __name__ == "__main__": ] # this will silently accept alternative providers of msgpack # if they are already installed - if "msgpack" in detectPrereqs(): + if "msgpack" in detectPrereqs(False): installRequires.append("msgpack-python") - elif "umsgpack" in detectPrereqs(): + elif "umsgpack" in detectPrereqs(False): installRequires.append("umsgpack") else: packages += ['pybitmessage.fallback', 'pybitmessage.fallback.umsgpack'] From 5f0a1e05e9c09051a38dedbe1c52ed41e84f1830 Mon Sep 17 00:00:00 2001 From: that_lurker Date: Sun, 10 Sep 2017 15:40:01 +0300 Subject: [PATCH 201/407] Added pull request template --- PULL_REQUEST_TEMPLATE.md | 14 ++++++++++++++ 1 file changed, 14 insertions(+) create mode 100644 PULL_REQUEST_TEMPLATE.md diff --git a/PULL_REQUEST_TEMPLATE.md b/PULL_REQUEST_TEMPLATE.md new file mode 100644 index 00000000..91c3bc96 --- /dev/null +++ b/PULL_REQUEST_TEMPLATE.md @@ -0,0 +1,14 @@ +## Code contributions to the Bitmessage project + +- try to explain what the code is about +- try to follow [PEP0008](https://www.python.org/dev/peps/pep-0008/) +- make the pull request against the ["v0.6" branch](https://github.com/Bitmessage/PyBitmessage/tree/v0.6) +- it should be possible to do a fast-forward merge of the pull requests +- PGP-sign the commits included in the pull request +- You can get paid for merged commits if you register at [Tip4Commit](https://tip4commit.com/github/Bitmessage/PyBitmessage) + +If for some reason you don't want to use github, you can submit the patch using Bitmessage to the "bitmessage" chan, or to one of the developers. +## Translations + +For helping with translations, please use [Transifex](https://www.transifex.com/bitmessage-project/pybitmessage/). There is no need to submit pull requests for translations. +For translating technical terms it is recommended to consult the [Microsoft Language Portal](https://www.microsoft.com/Language/en-US/Default.aspx). \ No newline at end of file From 24a9dc3b372c5032990bea333329e57efab00f39 Mon Sep 17 00:00:00 2001 From: Peter Surda Date: Tue, 19 Sep 2017 16:27:42 +0200 Subject: [PATCH 202/407] Put dependency checking into a separate file --- checkdeps.py | 215 +++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 215 insertions(+) create mode 100644 checkdeps.py diff --git a/checkdeps.py b/checkdeps.py new file mode 100644 index 00000000..65f8a787 --- /dev/null +++ b/checkdeps.py @@ -0,0 +1,215 @@ +"""Check dependendies and give recommendations about how to satisfy them""" + +from distutils.errors import CompileError +try: + from setuptools.dist import Distribution + from setuptools.extension import Extension + from setuptools.command.build_ext import build_ext + HAVE_SETUPTOOLS = True +except ImportError: + HAVE_SETUPTOOLS = False +from importlib import import_module +import os +import sys + +PACKAGE_MANAGER = { + "OpenBSD": "pkg_add", + "FreeBSD": "pkg install", + "Debian": "apt-get install", + "Ubuntu": "apt-get install", + "Ubuntu 12": "apt-get install", + "openSUSE": "zypper install", + "Fedora": "dnf install", + "Guix": "guix package -i", + "Gentoo": "emerge" +} + +PACKAGES = { + "PyQt4": { + "OpenBSD": "py-qt4", + "FreeBSD": "py27-qt4", + "Debian": "python-qt4", + "Ubuntu": "python-qt4", + "Ubuntu 12": "python-qt4", + "openSUSE": "python-qt", + "Fedora": "PyQt4", + "Guix": "python2-pyqt@4.11.4", + "Gentoo": "dev-python/PyQt4", + 'optional': True, + 'description': "You only need PyQt if you want to use the GUI. " \ + "When only running as a daemon, this can be skipped.\n" \ + "However, you would have to install it manually " \ + "because setuptools does not support PyQt." + }, + "msgpack": { + "OpenBSD": "py-msgpack", + "FreeBSD": "py27-msgpack-python", + "Debian": "python-msgpack", + "Ubuntu": "python-msgpack", + "Ubuntu 12": "msgpack-python", + "openSUSE": "python-msgpack-python", + "Fedora": "python2-msgpack", + "Guix": "python2-msgpack", + "Gentoo": "dev-python/msgpack", + "optional": True, + "description": "python-msgpack is recommended for improved performance of message encoding/decoding" + }, + "pyopencl": { + "FreeBSD": "py27-pyopencl", + "Debian": "python-pyopencl", + "Ubuntu": "python-pyopencl", + "Ubuntu 12": "python-pyopencl", + "Fedora": "python2-pyopencl", + "openSUSE": "", + "OpenBSD": "", + "Guix": "", + "Gentoo": "dev-python/pyopencl", + "optional": True, + 'description': "If you install pyopencl, you will be able to use " \ + "GPU acceleration for proof of work. \n" \ + "You also need a compatible GPU and drivers." + }, + "setuptools": { + "OpenBSD": "py-setuptools", + "FreeBSD": "py27-setuptools", + "Debian": "python-setuptools", + "Ubuntu": "python-setuptools", + "Ubuntu 12": "python-setuptools", + "Fedora": "python2-setuptools", + "openSUSE": "python-setuptools", + "Guix": "python2-setuptools", + "Gentoo": "", + "optional": False, + } +} + +COMPILING = { + "Debian": "build-essential libssl-dev", + "Ubuntu": "build-essential libssl-dev", + "Fedora": "gcc-c++ redhat-rpm-config python-devel openssl-devel", + "openSUSE": "gcc-c++ libopenssl-devel python-devel", + "optional": False, +} + +def detectOSRelease(): + with open("/etc/os-release", 'r') as osRelease: + version = None + for line in osRelease: + if line.startswith("NAME="): + line = line.lower() + if "fedora" in line: + detectOS.result = "Fedora" + elif "opensuse" in line: + detectOS.result = "openSUSE" + elif "ubuntu" in line: + detectOS.result = "Ubuntu" + elif "debian" in line: + detectOS.result = "Debian" + elif "gentoo" in line or "calculate" in line: + detectOS.result = "Gentoo" + else: + detectOS.result = None + if line.startswith("VERSION_ID="): + try: + version = float(line.split("\"")[1]) + except ValueError: + pass + if detectOS.result == "Ubuntu" and version < 14: + detectOS.result = "Ubuntu 12" + +def detectOS(): + if detectOS.result is not None: + return detectOS.result + if sys.platform.startswith('openbsd'): + detectOS.result = "OpenBSD" + elif sys.platform.startswith('freebsd'): + detectOS.result = "FreeBSD" + elif sys.platform.startswith('win'): + detectOS.result = "Windows" + elif os.path.isfile("/etc/os-release"): + detectOSRelease() + elif os.path.isfile("/etc/config.scm"): + detectOS.result = "Guix" + return detectOS.result + +def detectPrereqs(missing=True): + available = [] + for module in PACKAGES: + try: + import_module(module) + if not missing: + available.append(module) + except ImportError: + if missing: + available.append(module) + return available + +def prereqToPackages(): + if not detectPrereqs(): + return + print "%s %s" % ( + PACKAGE_MANAGER[detectOS()], " ".join( + PACKAGES[x][detectOS()] for x in detectPrereqs())) + +def compilerToPackages(): + if not detectOS() in COMPILING: + return + print "%s %s" % ( + PACKAGE_MANAGER[detectOS.result], COMPILING[detectOS.result]) + +def testCompiler(): + if not HAVE_SETUPTOOLS: + # silent, we can't test without setuptools + return True + + bitmsghash = Extension( + 'bitmsghash', + sources=['src/bitmsghash/bitmsghash.cpp'], + libraries=['pthread', 'crypto'], + ) + + dist = Distribution() + dist.ext_modules = [bitmsghash] + cmd = build_ext(dist) + cmd.initialize_options() + cmd.finalize_options() + cmd.force = True + try: + cmd.run() + except CompileError: + return False + else: + fullPath = os.path.join(cmd.build_lib, cmd.get_ext_filename("bitmsghash")) + return os.path.isfile(fullPath) + +detectOS.result = None +prereqs = detectPrereqs() + +compiler = testCompiler() + +if (not compiler or prereqs) and detectOS() in PACKAGE_MANAGER: + print "It looks like you're using %s. " \ + "It is highly recommended to use the package manager\n" \ + "to install the missing dependencies." % (detectOS.result) + +if not compiler: + print "Building the bitmsghash module failed.\n" \ + "You may be missing a C++ compiler and/or the OpenSSL headers." + +if prereqs: + mandatory = list(x for x in prereqs if "optional" not in PACKAGES[x] or not PACKAGES[x]["optional"]) + optional = list(x for x in prereqs if "optional" in PACKAGES[x] and PACKAGES[x]["optional"]) + if mandatory: + print "Missing mandatory dependencies: %s" % (" ".join(mandatory)) + if optional: + print "Missing optional dependencies: %s" % (" ".join(optional)) + for package in optional: + print PACKAGES[package].get('description') + +if (not compiler or prereqs) and detectOS() in PACKAGE_MANAGER: + print "You can install the missing dependencies by running, as root:" + if not compiler: + compilerToPackages() + prereqToPackages() +else: + print "All the dependencies satisfied, you can install PyBitmessage" From ef8f40ccc4abb677e96243ab096d0c818c80e8f3 Mon Sep 17 00:00:00 2001 From: Dmitri Bogomolov <4glitch@gmail.com> Date: Fri, 10 Mar 2017 01:00:54 +0200 Subject: [PATCH 203/407] Moved notifications to plugins (including sound) --- setup.py | 17 ++- src/bitmessageqt/__init__.py | 194 +++++++++++++--------------- src/plugins/notification_notify2.py | 13 ++ src/plugins/plugin.py | 8 +- src/plugins/sound_playfile.py | 37 ++++++ 5 files changed, 160 insertions(+), 109 deletions(-) create mode 100644 src/plugins/notification_notify2.py create mode 100644 src/plugins/sound_playfile.py diff --git a/setup.py b/setup.py index 9357284b..c7f7fab5 100644 --- a/setup.py +++ b/setup.py @@ -254,7 +254,9 @@ if __name__ == "__main__": install_requires=installRequires, extras_require={ 'qrcode': ['qrcode'], - 'pyopencl': ['pyopencl'] + 'pyopencl': ['pyopencl'], + 'notify2': ['pygobject', 'notify2'], + 'sound:platform_system=="Windows"': ['winsound'] }, classifiers=[ "License :: OSI Approved :: MIT License" @@ -278,9 +280,16 @@ if __name__ == "__main__": 'popMenuYourIdentities.qrcode = ' 'pybitmessage.plugins.qrcodeui [qrcode]' ], - # 'console_scripts': [ - # 'pybitmessage = pybitmessage.bitmessagemain:main' - # ] + 'notification.message': [ + 'notify2 = pybitmessage.plugins.notification_notify2' + '[notify2]' + ], + 'notification.sound': [ + 'fallback = pybitmessage.plugins.sound_playfile [sound]' + ], + # 'console_scripts': [ + # 'pybitmessage = pybitmessage.bitmessagemain:main' + # ] }, scripts=['src/pybitmessage'], cmdclass={'install': InstallCmd} diff --git a/src/bitmessageqt/__init__.py b/src/bitmessageqt/__init__.py index 014831bf..44dcb8ec 100644 --- a/src/bitmessageqt/__init__.py +++ b/src/bitmessageqt/__init__.py @@ -4,8 +4,6 @@ try: import gi gi.require_version('MessagingMenu', '1.0') from gi.repository import MessagingMenu - gi.require_version('Notify', '0.7') - from gi.repository import Notify withMessagingMenu = True except (ImportError, ValueError): MessagingMenu = None @@ -62,9 +60,8 @@ import platform import textwrap import debug import random -import subprocess import string -import datetime +from datetime import datetime, timedelta from helper_sql import * import helper_search import l10n @@ -153,7 +150,7 @@ class MyForm(settingsmixin.SMainWindow): SOUND_CONNECTION_GREEN = 5 # the last time that a message arrival sound was played - lastSoundTime = datetime.datetime.now() - datetime.timedelta(days=1) + lastSoundTime = datetime.now() - timedelta(days=1) # the maximum frequency of message sounds in seconds maxSoundFrequencySec = 60 @@ -1368,39 +1365,41 @@ class MyForm(settingsmixin.SMainWindow): # returns true if the given sound category is a connection sound # rather than a received message sound def isConnectionSound(self, category): - if (category is self.SOUND_CONNECTED or - category is self.SOUND_DISCONNECTED or - category is self.SOUND_CONNECTION_GREEN): - return True - return False + return category in ( + self.SOUND_CONNECTED, + self.SOUND_DISCONNECTED, + self.SOUND_CONNECTION_GREEN + ) # play a sound def playSound(self, category, label): # filename of the sound to be played soundFilename = None - # whether to play a sound or not - play = True + def _choose_ext(basename): + for ext in ('.wav', '.mp3', '.oga'): + if os.path.isfile(basename + ext): + return ext # if the address had a known label in the address book - if label is not None: + if label: # Does a sound file exist for this particular contact? - if (os.path.isfile(state.appdata + 'sounds/' + label + '.wav') or - os.path.isfile(state.appdata + 'sounds/' + label + '.mp3')): - soundFilename = state.appdata + 'sounds/' + label - - # Avoid making sounds more frequently than the threshold. - # This suppresses playing sounds repeatedly when there - # are many new messages - if (soundFilename is None and - not self.isConnectionSound(category)): - # elapsed time since the last sound was played - dt = datetime.datetime.now() - self.lastSoundTime - # suppress sounds which are more frequent than the threshold - if dt.total_seconds() < self.maxSoundFrequencySec: - play = False + soundFilename = state.appdata + 'sounds/' + label + ext = _choose_ext(soundFilename) + if not ext: + soundFilename = None if soundFilename is None: + # Avoid making sounds more frequently than the threshold. + # This suppresses playing sounds repeatedly when there + # are many new messages + if not self.isConnectionSound(category): + # elapsed time since the last sound was played + dt = datetime.now() - self.lastSoundTime + # suppress sounds which are more frequent than the threshold + if dt.total_seconds() < self.maxSoundFrequencySec: + return + # the sound is for an address which exists in the address book if category is self.SOUND_KNOWN: soundFilename = state.appdata + 'sounds/known' @@ -1415,75 +1414,55 @@ class MyForm(settingsmixin.SMainWindow): soundFilename = state.appdata + 'sounds/disconnected' # sound when the connection status becomes green elif category is self.SOUND_CONNECTION_GREEN: - soundFilename = state.appdata + 'sounds/green' + soundFilename = state.appdata + 'sounds/green' - if soundFilename is not None and play is True: - if not self.isConnectionSound(category): - # record the last time that a received message sound was played - self.lastSoundTime = datetime.datetime.now() + if soundFilename is None: + return - # if not wav then try mp3 format - if not os.path.isfile(soundFilename + '.wav'): - soundFilename = soundFilename + '.mp3' - else: - soundFilename = soundFilename + '.wav' + if not self.isConnectionSound(category): + # record the last time that a received message sound was played + self.lastSoundTime = datetime.now() - if os.path.isfile(soundFilename): - if 'linux' in sys.platform: - # Note: QSound was a nice idea but it didn't work - if '.mp3' in soundFilename: - gst_available=False - try: - subprocess.call(["gst123", soundFilename], - stdin=subprocess.PIPE, - stdout=subprocess.PIPE) - gst_available=True - except: - logger.warning("WARNING: gst123 must be installed in order to play mp3 sounds") - if not gst_available: - try: - subprocess.call(["mpg123", soundFilename], - stdin=subprocess.PIPE, - stdout=subprocess.PIPE) - gst_available=True - except: - logger.warning("WARNING: mpg123 must be installed in order to play mp3 sounds") - else: - try: - subprocess.call(["aplay", soundFilename], - stdin=subprocess.PIPE, - stdout=subprocess.PIPE) - except: - logger.warning("WARNING: aplay must be installed in order to play WAV sounds") - elif sys.platform[0:3] == 'win': - # use winsound on Windows - import winsound - winsound.PlaySound(soundFilename, winsound.SND_FILENAME) + try: # try already known format + soundFilename += ext + except (TypeError, NameError): + ext = _choose_ext(soundFilename) + if not ext: + return + + soundFilename += ext + + self._player(soundFilename) # initialise the message notifier def notifierInit(self): - if withMessagingMenu: - Notify.init('pybitmessage') - - # shows a notification - def notifierShow(self, title, subtitle, fromCategory, label): - self.playSound(fromCategory, label) - - if withMessagingMenu: - n = Notify.Notification.new( - title, subtitle, 'notification-message-email') - try: - n.show() - except: - # n.show() has been known to throw this exception: - # gi._glib.GError: GDBus.Error:org.freedesktop.Notifications. - # MaxNotificationsExceeded: Exceeded maximum number of - # notifications - pass - return - else: + def _simple_notify( + title, subtitle, category, label=None, icon=None): self.tray.showMessage(title, subtitle, 1, 2000) + self._notifier = _simple_notify + self._player = QtGui.QSound.play + + if not get_plugins: + return + + for plugin in get_plugins('notification.message'): + self._notifier = plugin + break + + if not QtGui.QSound.isAvailable(): + for plugin in get_plugins('notification.sound'): + self._player = plugin + break + else: + logger.warning("No sound player plugin found!") + + def notifierShow( + self, title, subtitle, category, label=None, icon=None): + self.playSound(category, label) + self._notifier( + unicode(title), unicode(subtitle), category, label, icon) + # tree def treeWidgetKeyPressEvent(self, event): return self.handleKeyPress(event, self.getCurrentTreeWidget()) @@ -1663,15 +1642,18 @@ class MyForm(settingsmixin.SMainWindow): def setStatusIcon(self, color): # print 'setting status icon color' + _notifications_enabled = not BMConfigParser().getboolean( + 'bitmessagesettings', 'hidetrayconnectionnotifications') if color == 'red': self.pushButtonStatusIcon.setIcon( QIcon(":/newPrefix/images/redicon.png")) shared.statusIconColor = 'red' # if the connection is lost then show a notification - if self.connected and not BMConfigParser().getboolean('bitmessagesettings', 'hidetrayconnectionnotifications'): - self.notifierShow('Bitmessage', unicode(_translate( - "MainWindow", "Connection lost").toUtf8(),'utf-8'), - self.SOUND_DISCONNECTED, None) + if self.connected and _notifications_enabled: + self.notifierShow( + 'Bitmessage', + _translate("MainWindow", "Connection lost"), + self.SOUND_DISCONNECTED) if not BMConfigParser().safeGetBoolean('bitmessagesettings', 'upnp') and \ BMConfigParser().get('bitmessagesettings', 'socksproxytype') == "none": self.statusBar().showMessage(_translate( @@ -1689,10 +1671,11 @@ class MyForm(settingsmixin.SMainWindow): ":/newPrefix/images/yellowicon.png")) shared.statusIconColor = 'yellow' # if a new connection has been established then show a notification - if not self.connected and not BMConfigParser().getboolean('bitmessagesettings', 'hidetrayconnectionnotifications'): - self.notifierShow('Bitmessage', unicode(_translate( - "MainWindow", "Connected").toUtf8(),'utf-8'), - self.SOUND_CONNECTED, None) + if not self.connected and _notifications_enabled: + self.notifierShow( + 'Bitmessage', + _translate("MainWindow", "Connected"), + self.SOUND_CONNECTED) self.connected = True if self.actionStatus is not None: @@ -1705,10 +1688,11 @@ class MyForm(settingsmixin.SMainWindow): self.pushButtonStatusIcon.setIcon( QIcon(":/newPrefix/images/greenicon.png")) shared.statusIconColor = 'green' - if not self.connected and not BMConfigParser().getboolean('bitmessagesettings', 'hidetrayconnectionnotifications'): - self.notifierShow('Bitmessage', unicode(_translate( - "MainWindow", "Connected").toUtf8(),'utf-8'), - self.SOUND_CONNECTION_GREEN, None) + if not self.connected and _notifications_enabled: + self.notifierShow( + 'Bitmessage', + _translate("MainWindow", "Connected"), + self.SOUND_CONNECTION_GREEN) self.connected = True if self.actionStatus is not None: @@ -2253,8 +2237,14 @@ class MyForm(settingsmixin.SMainWindow): else: acct = ret self.propagateUnreadCount(acct.address) - if BMConfigParser().getboolean('bitmessagesettings', 'showtraynotifications'): - self.notifierShow(unicode(_translate("MainWindow",'New Message').toUtf8(),'utf-8'), unicode(_translate("MainWindow",'From ').toUtf8(),'utf-8') + unicode(acct.fromLabel, 'utf-8'), self.SOUND_UNKNOWN, None) + if BMConfigParser().getboolean( + 'bitmessagesettings', 'showtraynotifications'): + self.notifierShow( + _translate("MainWindow", "New Message"), + _translate("MainWindow", "From %1").arg( + unicode(acct.fromLabel, 'utf-8')), + self.SOUND_UNKNOWN + ) if self.getCurrentAccount() is not None and ((self.getCurrentFolder(treeWidget) != "inbox" and self.getCurrentFolder(treeWidget) is not None) or self.getCurrentAccount(treeWidget) != acct.address): # Ubuntu should notify of new message irespective of whether it's in current message list or not self.ubuntuMessagingMenuUpdate(True, None, acct.toLabel) diff --git a/src/plugins/notification_notify2.py b/src/plugins/notification_notify2.py new file mode 100644 index 00000000..90f09df3 --- /dev/null +++ b/src/plugins/notification_notify2.py @@ -0,0 +1,13 @@ +# -*- coding: utf-8 -*- + +import gi +gi.require_version('Notify', '0.7') +from gi.repository import Notify + +Notify.init('pybitmessage') + + +def connect_plugin(title, subtitle, category, label, icon): + if not icon: + icon = 'mail-message-new' if category == 2 else 'pybitmessage' + Notify.Notification.new(title, subtitle, icon).show() diff --git a/src/plugins/plugin.py b/src/plugins/plugin.py index 288be48a..395f0e9d 100644 --- a/src/plugins/plugin.py +++ b/src/plugins/plugin.py @@ -3,10 +3,12 @@ import pkg_resources -def get_plugins(group, point, name=None): +def get_plugins(group, point='', name=None): for plugin in pkg_resources.iter_entry_points(group): - if plugin.name.startswith(point): + if plugin.name == name or plugin.name.startswith(point): try: yield plugin.load().connect_plugin - except (AttributeError, pkg_resources.DistributionNotFound): + except (AttributeError, + pkg_resources.DistributionNotFound, + pkg_resources.UnknownExtra): continue diff --git a/src/plugins/sound_playfile.py b/src/plugins/sound_playfile.py new file mode 100644 index 00000000..03a164af --- /dev/null +++ b/src/plugins/sound_playfile.py @@ -0,0 +1,37 @@ +# -*- coding: utf-8 -*- + + +try: + import winsound + + def connect_plugin(sound_file): + winsound.PlaySound(sound_file, winsound.SND_FILENAME) +except ImportError: + import os + import subprocess + + play_cmd = {} + + def connect_plugin(sound_file): + global play_cmd + + ext = os.path.splitext(sound_file)[-1] + try: + subprocess.call([play_cmd[ext], sound_file]) + return + except (KeyError, AttributeError): + pass + + programs = ['gst123'] + if ext == '.wav': + programs.append('aplay') + elif ext == '.mp3': + programs += ['mpg123', 'mpg321', 'mpg321-mpg123'] + for cmd in programs: + try: + subprocess.call([cmd, sound_file]) + except OSError: + pass # log here! + else: + play_cmd[ext] = cmd + break From be716bf228685f11742dcf15a0d9189dddd3d6a8 Mon Sep 17 00:00:00 2001 From: Dmitri Bogomolov <4glitch@gmail.com> Date: Fri, 10 Mar 2017 16:45:46 +0200 Subject: [PATCH 204/407] Improved and documented plugin module --- src/bitmessageqt/__init__.py | 16 +++++++++------- src/plugins/plugin.py | 29 +++++++++++++++++++++++++---- 2 files changed, 34 insertions(+), 11 deletions(-) diff --git a/src/bitmessageqt/__init__.py b/src/bitmessageqt/__init__.py index 44dcb8ec..39202d17 100644 --- a/src/bitmessageqt/__init__.py +++ b/src/bitmessageqt/__init__.py @@ -86,7 +86,7 @@ import throttle from version import softwareVersion try: - from plugins.plugin import get_plugins + from plugins.plugin import get_plugin, get_plugins except ImportError: get_plugins = False @@ -1441,19 +1441,21 @@ class MyForm(settingsmixin.SMainWindow): self.tray.showMessage(title, subtitle, 1, 2000) self._notifier = _simple_notify + # does nothing if isAvailable returns false self._player = QtGui.QSound.play if not get_plugins: return - for plugin in get_plugins('notification.message'): - self._notifier = plugin - break + _plugin = get_plugin('notification.message') + if _plugin: + self._notifier = _plugin if not QtGui.QSound.isAvailable(): - for plugin in get_plugins('notification.sound'): - self._player = plugin - break + _plugin = get_plugin( + 'notification.sound', 'file', fallback='file.fallback') + if _plugin: + self._player = _plugin else: logger.warning("No sound player plugin found!") diff --git a/src/plugins/plugin.py b/src/plugins/plugin.py index 395f0e9d..ba14b836 100644 --- a/src/plugins/plugin.py +++ b/src/plugins/plugin.py @@ -3,12 +3,33 @@ import pkg_resources -def get_plugins(group, point='', name=None): - for plugin in pkg_resources.iter_entry_points(group): - if plugin.name == name or plugin.name.startswith(point): +def get_plugins(group, point='', name=None, fallback=None): + """ + Iterate through plugins (`connect_plugin` attribute of entry point) + which name starts with `point` or equals to `name`. + If `fallback` kwarg specified, plugin with that name yield last. + """ + for ep in pkg_resources.iter_entry_points(group): + if name and ep.name == name or ep.name.startswith(point): try: - yield plugin.load().connect_plugin + plugin = ep.load().connect_plugin + if ep.name == fallback: + _fallback = plugin + else: + yield plugin except (AttributeError, + ImportError, + ValueError, pkg_resources.DistributionNotFound, pkg_resources.UnknownExtra): continue + try: + yield _fallback + except NameError: + pass + + +def get_plugin(*args, **kwargs): + """Returns first available plugin `from get_plugins()` if any.""" + for plugin in get_plugins(*args, **kwargs): + return plugin From 91eb75b1407ad18cb4919c9786d6d99f32b8caac Mon Sep 17 00:00:00 2001 From: Dmitri Bogomolov <4glitch@gmail.com> Date: Fri, 10 Mar 2017 18:34:31 +0200 Subject: [PATCH 205/407] gst-play-1.0 is another player program which bundled with gstreamer 1.0 --- src/plugins/sound_playfile.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/plugins/sound_playfile.py b/src/plugins/sound_playfile.py index 03a164af..65d5dda9 100644 --- a/src/plugins/sound_playfile.py +++ b/src/plugins/sound_playfile.py @@ -22,7 +22,7 @@ except ImportError: except (KeyError, AttributeError): pass - programs = ['gst123'] + programs = ['gst123', 'gst-play-1.0'] if ext == '.wav': programs.append('aplay') elif ext == '.mp3': From 84a903f116e99403023de753b1db980312446b8f Mon Sep 17 00:00:00 2001 From: Dmitri Bogomolov <4glitch@gmail.com> Date: Sat, 11 Mar 2017 13:33:51 +0200 Subject: [PATCH 206/407] Redirected output of the player programs to /dev/null --- src/plugins/sound_playfile.py | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/src/plugins/sound_playfile.py b/src/plugins/sound_playfile.py index 65d5dda9..c8216d07 100644 --- a/src/plugins/sound_playfile.py +++ b/src/plugins/sound_playfile.py @@ -12,13 +12,17 @@ except ImportError: play_cmd = {} + def _subprocess(*args): + FNULL = open(os.devnull, 'wb') + subprocess.call( + args, stdout=FNULL, stderr=subprocess.STDOUT, close_fds=True) + def connect_plugin(sound_file): global play_cmd ext = os.path.splitext(sound_file)[-1] try: - subprocess.call([play_cmd[ext], sound_file]) - return + return _subprocess(play_cmd[ext], sound_file) except (KeyError, AttributeError): pass @@ -29,7 +33,7 @@ except ImportError: programs += ['mpg123', 'mpg321', 'mpg321-mpg123'] for cmd in programs: try: - subprocess.call([cmd, sound_file]) + _subprocess(cmd, sound_file) except OSError: pass # log here! else: From 289a6c5bfa9218e0bec83bbc970160d56525ff5b Mon Sep 17 00:00:00 2001 From: Dmitri Bogomolov <4glitch@gmail.com> Date: Sat, 11 Mar 2017 13:40:33 +0200 Subject: [PATCH 207/407] Added support for sound notification plugins which use the desktop sound theme, with pycanberra for example. Plugin name should start with 'theme' in that case, whereas the name of plugins playing the sound file starts with 'file'. --- setup.py | 3 ++- src/bitmessageqt/__init__.py | 13 +++++++++++-- 2 files changed, 13 insertions(+), 3 deletions(-) diff --git a/setup.py b/setup.py index c7f7fab5..eb79c3b7 100644 --- a/setup.py +++ b/setup.py @@ -285,7 +285,8 @@ if __name__ == "__main__": '[notify2]' ], 'notification.sound': [ - 'fallback = pybitmessage.plugins.sound_playfile [sound]' + 'file.fallback = pybitmessage.plugins.sound_playfile' + '[sound]' ], # 'console_scripts': [ # 'pybitmessage = pybitmessage.bitmessagemain:main' diff --git a/src/bitmessageqt/__init__.py b/src/bitmessageqt/__init__.py index 39202d17..fd0bcdc1 100644 --- a/src/bitmessageqt/__init__.py +++ b/src/bitmessageqt/__init__.py @@ -1387,6 +1387,7 @@ class MyForm(settingsmixin.SMainWindow): soundFilename = state.appdata + 'sounds/' + label ext = _choose_ext(soundFilename) if not ext: + category = self.SOUND_KNOWN soundFilename = None if soundFilename is None: @@ -1417,6 +1418,7 @@ class MyForm(settingsmixin.SMainWindow): soundFilename = state.appdata + 'sounds/green' if soundFilename is None: + logger.warning("Probably wrong category number in playSound()") return if not self.isConnectionSound(category): @@ -1428,7 +1430,10 @@ class MyForm(settingsmixin.SMainWindow): except (TypeError, NameError): ext = _choose_ext(soundFilename) if not ext: - return + try: # if no user sound file found try to play from theme + return self._theme_player(category, label) + except TypeError: + return soundFilename += ext @@ -1450,6 +1455,10 @@ class MyForm(settingsmixin.SMainWindow): _plugin = get_plugin('notification.message') if _plugin: self._notifier = _plugin + else: + logger.warning("No notification.message plugin found") + + self._theme_player = get_plugin('notification.sound', 'theme') if not QtGui.QSound.isAvailable(): _plugin = get_plugin( @@ -1457,7 +1466,7 @@ class MyForm(settingsmixin.SMainWindow): if _plugin: self._player = _plugin else: - logger.warning("No sound player plugin found!") + logger.warning("No notification.sound plugin found") def notifierShow( self, title, subtitle, category, label=None, icon=None): From cd8171887195edd089826be33e19dc68e836867d Mon Sep 17 00:00:00 2001 From: Dmitri Bogomolov <4glitch@gmail.com> Date: Wed, 15 Mar 2017 15:56:47 +0200 Subject: [PATCH 208/407] Moved sound category constants to the separate module `sound` for importing from the sound theme plugins. --- src/bitmessageqt/__init__.py | 42 +++++++++++------------------------- src/bitmessageqt/sound.py | 19 ++++++++++++++++ 2 files changed, 32 insertions(+), 29 deletions(-) create mode 100644 src/bitmessageqt/sound.py diff --git a/src/bitmessageqt/__init__.py b/src/bitmessageqt/__init__.py index fd0bcdc1..8d126812 100644 --- a/src/bitmessageqt/__init__.py +++ b/src/bitmessageqt/__init__.py @@ -84,6 +84,7 @@ import state from statusbar import BMStatusBar import throttle from version import softwareVersion +import sound try: from plugins.plugin import get_plugin, get_plugins @@ -141,14 +142,6 @@ def change_translation(newlocale): class MyForm(settingsmixin.SMainWindow): - # sound type constants - SOUND_NONE = 0 - SOUND_KNOWN = 1 - SOUND_UNKNOWN = 2 - SOUND_CONNECTED = 3 - SOUND_DISCONNECTED = 4 - SOUND_CONNECTION_GREEN = 5 - # the last time that a message arrival sound was played lastSoundTime = datetime.now() - timedelta(days=1) @@ -1362,15 +1355,6 @@ class MyForm(settingsmixin.SMainWindow): # update the menu entries self.ubuntuMessagingMenuUnread(drawAttention) - # returns true if the given sound category is a connection sound - # rather than a received message sound - def isConnectionSound(self, category): - return category in ( - self.SOUND_CONNECTED, - self.SOUND_DISCONNECTED, - self.SOUND_CONNECTION_GREEN - ) - # play a sound def playSound(self, category, label): # filename of the sound to be played @@ -1387,14 +1371,14 @@ class MyForm(settingsmixin.SMainWindow): soundFilename = state.appdata + 'sounds/' + label ext = _choose_ext(soundFilename) if not ext: - category = self.SOUND_KNOWN + category = sound.SOUND_KNOWN soundFilename = None if soundFilename is None: # Avoid making sounds more frequently than the threshold. # This suppresses playing sounds repeatedly when there # are many new messages - if not self.isConnectionSound(category): + if not sound.is_connection_sound(category): # elapsed time since the last sound was played dt = datetime.now() - self.lastSoundTime # suppress sounds which are more frequent than the threshold @@ -1402,26 +1386,26 @@ class MyForm(settingsmixin.SMainWindow): return # the sound is for an address which exists in the address book - if category is self.SOUND_KNOWN: + if category is sound.SOUND_KNOWN: soundFilename = state.appdata + 'sounds/known' # the sound is for an unknown address - elif category is self.SOUND_UNKNOWN: + elif category is sound.SOUND_UNKNOWN: soundFilename = state.appdata + 'sounds/unknown' # initial connection sound - elif category is self.SOUND_CONNECTED: + elif category is sound.SOUND_CONNECTED: soundFilename = state.appdata + 'sounds/connected' # disconnected sound - elif category is self.SOUND_DISCONNECTED: + elif category is sound.SOUND_DISCONNECTED: soundFilename = state.appdata + 'sounds/disconnected' # sound when the connection status becomes green - elif category is self.SOUND_CONNECTION_GREEN: + elif category is sound.SOUND_CONNECTION_GREEN: soundFilename = state.appdata + 'sounds/green' if soundFilename is None: logger.warning("Probably wrong category number in playSound()") return - if not self.isConnectionSound(category): + if not sound.is_connection_sound(category): # record the last time that a received message sound was played self.lastSoundTime = datetime.now() @@ -1664,7 +1648,7 @@ class MyForm(settingsmixin.SMainWindow): self.notifierShow( 'Bitmessage', _translate("MainWindow", "Connection lost"), - self.SOUND_DISCONNECTED) + sound.SOUND_DISCONNECTED) if not BMConfigParser().safeGetBoolean('bitmessagesettings', 'upnp') and \ BMConfigParser().get('bitmessagesettings', 'socksproxytype') == "none": self.statusBar().showMessage(_translate( @@ -1686,7 +1670,7 @@ class MyForm(settingsmixin.SMainWindow): self.notifierShow( 'Bitmessage', _translate("MainWindow", "Connected"), - self.SOUND_CONNECTED) + sound.SOUND_CONNECTED) self.connected = True if self.actionStatus is not None: @@ -1703,7 +1687,7 @@ class MyForm(settingsmixin.SMainWindow): self.notifierShow( 'Bitmessage', _translate("MainWindow", "Connected"), - self.SOUND_CONNECTION_GREEN) + sound.SOUND_CONNECTION_GREEN) self.connected = True if self.actionStatus is not None: @@ -2254,7 +2238,7 @@ class MyForm(settingsmixin.SMainWindow): _translate("MainWindow", "New Message"), _translate("MainWindow", "From %1").arg( unicode(acct.fromLabel, 'utf-8')), - self.SOUND_UNKNOWN + sound.SOUND_UNKNOWN ) if self.getCurrentAccount() is not None and ((self.getCurrentFolder(treeWidget) != "inbox" and self.getCurrentFolder(treeWidget) is not None) or self.getCurrentAccount(treeWidget) != acct.address): # Ubuntu should notify of new message irespective of whether it's in current message list or not diff --git a/src/bitmessageqt/sound.py b/src/bitmessageqt/sound.py new file mode 100644 index 00000000..4b6aaf00 --- /dev/null +++ b/src/bitmessageqt/sound.py @@ -0,0 +1,19 @@ +# -*- coding: utf-8 -*- + +# sound type constants +SOUND_NONE = 0 +SOUND_KNOWN = 1 +SOUND_UNKNOWN = 2 +SOUND_CONNECTED = 3 +SOUND_DISCONNECTED = 4 +SOUND_CONNECTION_GREEN = 5 + + +# returns true if the given sound category is a connection sound +# rather than a received message sound +def is_connection_sound(category): + return category in ( + SOUND_CONNECTED, + SOUND_DISCONNECTED, + SOUND_CONNECTION_GREEN + ) From c8a47b988fb3c464d8d6e86b8032f09211314910 Mon Sep 17 00:00:00 2001 From: Dmitri Bogomolov <4glitch@gmail.com> Date: Thu, 23 Mar 2017 19:04:56 +0200 Subject: [PATCH 209/407] Moved "Ubuntu" MessagingMenu code into indicator_libmessaging plugin --- src/bitmessageqt/__init__.py | 222 +++++--------------------- src/plugins/indicator_libmessaging.py | 71 ++++++++ 2 files changed, 109 insertions(+), 184 deletions(-) create mode 100644 src/plugins/indicator_libmessaging.py diff --git a/src/bitmessageqt/__init__.py b/src/bitmessageqt/__init__.py index 8d126812..942ed3de 100644 --- a/src/bitmessageqt/__init__.py +++ b/src/bitmessageqt/__init__.py @@ -1,19 +1,10 @@ from debug import logger -withMessagingMenu = False -try: - import gi - gi.require_version('MessagingMenu', '1.0') - from gi.repository import MessagingMenu - withMessagingMenu = True -except (ImportError, ValueError): - MessagingMenu = None try: from PyQt4 import QtCore, QtGui from PyQt4.QtCore import * from PyQt4.QtGui import * from PyQt4.QtNetwork import QLocalSocket, QLocalServer - except Exception as err: logmsg = 'PyBitmessage requires PyQt unless you want to run it as a daemon and interact with it using the API. You can download it from http://www.riverbankcomputing.com/software/pyqt/download or by searching Google for \'PyQt Download\' (without quotes).' logger.critical(logmsg, exc_info=True) @@ -30,7 +21,7 @@ import shared from bitmessageui import * from bmconfigparser import BMConfigParser import defaults -from namecoin import namecoinConnection, ensureNamecoinOptions +from namecoin import namecoinConnection from newaddressdialog import * from newaddresswizard import * from messageview import MessageView @@ -51,12 +42,10 @@ from iconglossary import * from connect import * import locale import sys -from time import strftime, localtime, gmtime import time import os import hashlib from pyelliptic.openssl import OpenSSL -import platform import textwrap import debug import random @@ -66,15 +55,13 @@ from helper_sql import * import helper_search import l10n import openclpow -import types -from utils import * -from collections import OrderedDict +from utils import str_broadcast_subscribers, avatarize from account import * -from class_objectHashHolder import objectHashHolder -from class_singleWorker import singleWorker from dialogs import AddAddressDialog from helper_generic import powQueueSize -from inventory import Inventory, PendingDownloadQueue, PendingUpload, PendingUploadDeadlineException +from inventory import ( + Inventory, PendingDownloadQueue, PendingUpload, + PendingUploadDeadlineException) import knownnodes import paths from proofofwork import getPowType @@ -140,6 +127,7 @@ def change_translation(newlocale): except: logger.error("Failed to set locale to %s", lang, exc_info=True) + class MyForm(settingsmixin.SMainWindow): # the last time that a message arrival sound was played @@ -148,8 +136,6 @@ class MyForm(settingsmixin.SMainWindow): # the maximum frequency of message sounds in seconds maxSoundFrequencySec = 60 - str_chan = '[chan]' - REPLY_TYPE_SENDER = 0 REPLY_TYPE_CHAN = 1 @@ -858,14 +844,6 @@ class MyForm(settingsmixin.SMainWindow): self.raise_() self.activateWindow() - # pointer to the application - # app = None - # The most recent message - newMessageItem = None - - # The most recent broadcast - newBroadcastItem = None - # show the application window def appIndicatorShow(self): if self.actionShow is None: @@ -895,32 +873,19 @@ class MyForm(settingsmixin.SMainWindow): self.appIndicatorShowOrHideWindow()""" # Show the program window and select inbox tab - def appIndicatorInbox(self, mm_app, source_id): + def appIndicatorInbox(self, item=None): self.appIndicatorShow() # select inbox self.ui.tabWidget.setCurrentIndex(0) - selectedItem = None - if source_id == 'Subscriptions': - # select unread broadcast - if self.newBroadcastItem is not None: - selectedItem = self.newBroadcastItem - self.newBroadcastItem = None - else: - # select unread message - if self.newMessageItem is not None: - selectedItem = self.newMessageItem - self.newMessageItem = None - # make it the current item - if selectedItem is not None: - try: - self.ui.tableWidgetInbox.setCurrentItem(selectedItem) - except Exception: - self.ui.tableWidgetInbox.setCurrentCell(0, 0) + self.ui.treeWidgetYourIdentities.setCurrentItem( + self.ui.treeWidgetYourIdentities.topLevelItem(0).child(0) + ) + + if item: + self.ui.tableWidgetInbox.setCurrentItem(item) self.tableWidgetInboxItemClicked() else: - # just select the first item self.ui.tableWidgetInbox.setCurrentCell(0, 0) - self.tableWidgetInboxItemClicked() # Show the program window and select send tab def appIndicatorSend(self): @@ -1218,142 +1183,21 @@ class MyForm(settingsmixin.SMainWindow): self.tray.setContextMenu(m) self.tray.show() - # Ubuntu Messaging menu object - mmapp = None - - # is the operating system Ubuntu? - def isUbuntu(self): - for entry in platform.uname(): - if "Ubuntu" in entry: - return True - return False - - # When an unread inbox row is selected on then clear the messaging menu - def ubuntuMessagingMenuClear(self, inventoryHash): - # if this isn't ubuntu then don't do anything - if not self.isUbuntu(): - return - - # has messageing menu been installed - if not withMessagingMenu: - return - - # if there are no items on the messaging menu then - # the subsequent query can be avoided - if not (self.mmapp.has_source("Subscriptions") or self.mmapp.has_source("Messages")): - return - - queryreturn = sqlQuery( - '''SELECT toaddress, read FROM inbox WHERE msgid=?''', inventoryHash) - for row in queryreturn: - toAddress, read = row - if not read: - if toAddress == str_broadcast_subscribers: - if self.mmapp.has_source("Subscriptions"): - self.mmapp.remove_source("Subscriptions") - else: - if self.mmapp.has_source("Messages"): - self.mmapp.remove_source("Messages") - # returns the number of unread messages and subscriptions def getUnread(self): - unreadMessages = 0 - unreadSubscriptions = 0 + counters = [0, 0] - queryreturn = sqlQuery( - '''SELECT msgid, toaddress, read FROM inbox where folder='inbox' ''') - for row in queryreturn: - msgid, toAddress, read = row - - try: - if toAddress == str_broadcast_subscribers: - toLabel = str_broadcast_subscribers - else: - toLabel = BMConfigParser().get(toAddress, 'label') - except: - toLabel = '' - if toLabel == '': - toLabel = toAddress + queryreturn = sqlQuery(''' + SELECT msgid, toaddress, read FROM inbox where folder='inbox' + ''') + for msgid, toAddress, read in queryreturn: if not read: - if toLabel == str_broadcast_subscribers: - # increment the unread subscriptions - unreadSubscriptions = unreadSubscriptions + 1 - else: - # increment the unread messages - unreadMessages = unreadMessages + 1 - return unreadMessages, unreadSubscriptions + # increment the unread subscriptions if True (1) + # else messages (0) + counters[toAddress == str_broadcast_subscribers] += 1 - # show the number of unread messages and subscriptions on the messaging - # menu - def ubuntuMessagingMenuUnread(self, drawAttention): - unreadMessages, unreadSubscriptions = self.getUnread() - # unread messages - if unreadMessages > 0: - self.mmapp.append_source( - "Messages", None, "Messages (" + str(unreadMessages) + ")") - if drawAttention: - self.mmapp.draw_attention("Messages") - - # unread subscriptions - if unreadSubscriptions > 0: - self.mmapp.append_source("Subscriptions", None, "Subscriptions (" + str( - unreadSubscriptions) + ")") - if drawAttention: - self.mmapp.draw_attention("Subscriptions") - - # initialise the Ubuntu messaging menu - def ubuntuMessagingMenuInit(self): - global withMessagingMenu - - # if this isn't ubuntu then don't do anything - if not self.isUbuntu(): - return - - # has messageing menu been installed - if not withMessagingMenu: - logger.warning('WARNING: MessagingMenu is not available. Is libmessaging-menu-dev installed?') - return - - # create the menu server - if withMessagingMenu: - try: - self.mmapp = MessagingMenu.App( - desktop_id='pybitmessage.desktop') - self.mmapp.register() - self.mmapp.connect('activate-source', self.appIndicatorInbox) - self.ubuntuMessagingMenuUnread(True) - except Exception: - withMessagingMenu = False - logger.warning('WARNING: messaging menu disabled') - - # update the Ubuntu messaging menu - def ubuntuMessagingMenuUpdate(self, drawAttention, newItem, toLabel): - # if this isn't ubuntu then don't do anything - if not self.isUbuntu(): - return - - # has messageing menu been installed - if not withMessagingMenu: - logger.warning('WARNING: messaging menu disabled or libmessaging-menu-dev not installed') - return - - # remember this item to that the messaging menu can find it - if toLabel == str_broadcast_subscribers: - self.newBroadcastItem = newItem - else: - self.newMessageItem = newItem - - # Remove previous messages and subscriptions entries, then recreate them - # There might be a better way to do it than this - if self.mmapp.has_source("Messages"): - self.mmapp.remove_source("Messages") - - if self.mmapp.has_source("Subscriptions"): - self.mmapp.remove_source("Subscriptions") - - # update the menu entries - self.ubuntuMessagingMenuUnread(drawAttention) + return counters # play a sound def playSound(self, category, label): @@ -1423,6 +1267,17 @@ class MyForm(settingsmixin.SMainWindow): self._player(soundFilename) + # Try init the distro specific appindicator, + # for example the Ubuntu MessagingMenu + def indicatorInit(self): + def _noop_update(*args, **kwargs): + pass + + try: + self.indicatorUpdate = get_plugin('indicator')(self) + except (NameError, TypeError): + self.indicatorUpdate = _noop_update + # initialise the message notifier def notifierInit(self): def _simple_notify( @@ -2241,8 +2096,10 @@ class MyForm(settingsmixin.SMainWindow): sound.SOUND_UNKNOWN ) if self.getCurrentAccount() is not None and ((self.getCurrentFolder(treeWidget) != "inbox" and self.getCurrentFolder(treeWidget) is not None) or self.getCurrentAccount(treeWidget) != acct.address): - # Ubuntu should notify of new message irespective of whether it's in current message list or not - self.ubuntuMessagingMenuUpdate(True, None, acct.toLabel) + # Ubuntu should notify of new message irespective of + # whether it's in current message list or not + self.indicatorUpdate(True, to_label=acct.toLabel) + # cannot find item to pass here ): if hasattr(acct, "feedback") and acct.feedback != GatewayAccount.ALL_OK: if acct.feedback == GatewayAccount.REGISTRATION_DENIED: self.dialog = EmailGatewayRegistrationDialog(self, _translate("EmailGatewayRegistrationDialog", "Registration failed:"), @@ -2836,9 +2693,6 @@ class MyForm(settingsmixin.SMainWindow): shutdown.doCleanShutdown() self.statusBar().showMessage(_translate("MainWindow", "Stopping notifications... %1%").arg(str(90))) self.tray.hide() - # unregister the messaging system - if self.mmapp is not None: - self.mmapp.unregister() self.statusBar().showMessage(_translate("MainWindow", "Shutdown imminent... %1%").arg(str(100))) shared.thisapp.cleanup() @@ -4510,7 +4364,7 @@ def run(): myapp = MyForm() myapp.appIndicatorInit(app) - myapp.ubuntuMessagingMenuInit() + myapp.indicatorInit() myapp.notifierInit() myapp._firstrun = BMConfigParser().safeGetBoolean( 'bitmessagesettings', 'dontconnect') diff --git a/src/plugins/indicator_libmessaging.py b/src/plugins/indicator_libmessaging.py new file mode 100644 index 00000000..96ab1516 --- /dev/null +++ b/src/plugins/indicator_libmessaging.py @@ -0,0 +1,71 @@ +# -*- coding: utf-8 -*- + +import gi +gi.require_version('MessagingMenu', '1.0') +from gi.repository import MessagingMenu + +from pybitmessage.bitmessageqt.utils import str_broadcast_subscribers +from pybitmessage.tr import _translate + + +class IndicatorLibmessaging(object): + def __init__(self, form): + try: + self.app = MessagingMenu.App(desktop_id='pybitmessage.desktop') + self.app.register() + self.app.connect('activate-source', self.activate) + except: + self.app = None + return + + self._menu = { + 'send': str(_translate('MainWindow', 'Send')), + 'messages': str(_translate('MainWindow', 'Messages')), + 'subscriptions': str(_translate('MainWindow', 'Subscriptions')) + } + + self.new_message_item = self.new_broadcast_item = None + self.form = form + self.show_unread() + + def __del__(self): + if self.app: + self.app.unregister() + + def activate(self, app, source): + self.form.appIndicatorInbox( + self.new_message_item if source == 'messages' + else self.new_broadcast_item + ) + + # show the number of unread messages and subscriptions + # on the messaging menu + def show_unread(self, draw_attention=False): + for source, count in zip( + ('messages', 'subscriptions'), + self.form.getUnread() + ): + if count > 0: + if self.app.has_source(source): + self.app.set_source_count(source, count) + else: + self.app.append_source_with_count( + source, None, self._menu[source], count) + if draw_attention: + self.app.draw_attention(source) + + # update the Ubuntu messaging menu + def __call__(self, draw_attention, item=None, to_label=None): + if not self.app: + return + # remember this item to that the activate() can find it + if item: + if to_label == str_broadcast_subscribers: + self.new_broadcast_item = item + else: + self.new_message_item = item + + self.show_unread(draw_attention) + + +connect_plugin = IndicatorLibmessaging From b77eb0c7e57a41917284a35d68caae0d987fadeb Mon Sep 17 00:00:00 2001 From: Dmitri Bogomolov <4glitch@gmail.com> Date: Fri, 24 Mar 2017 14:31:27 +0200 Subject: [PATCH 210/407] Namespace 'bitmessage' for plugins entry points --- setup.py | 6 +++--- src/plugins/plugin.py | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/setup.py b/setup.py index eb79c3b7..06d49ba5 100644 --- a/setup.py +++ b/setup.py @@ -276,15 +276,15 @@ if __name__ == "__main__": ext_modules=[bitmsghash], zip_safe=False, entry_points={ - 'gui.menu': [ + 'bitmessage.gui.menu': [ 'popMenuYourIdentities.qrcode = ' 'pybitmessage.plugins.qrcodeui [qrcode]' ], - 'notification.message': [ + 'bitmessage.notification.message': [ 'notify2 = pybitmessage.plugins.notification_notify2' '[notify2]' ], - 'notification.sound': [ + 'bitmessage.notification.sound': [ 'file.fallback = pybitmessage.plugins.sound_playfile' '[sound]' ], diff --git a/src/plugins/plugin.py b/src/plugins/plugin.py index ba14b836..6601adaf 100644 --- a/src/plugins/plugin.py +++ b/src/plugins/plugin.py @@ -9,7 +9,7 @@ def get_plugins(group, point='', name=None, fallback=None): which name starts with `point` or equals to `name`. If `fallback` kwarg specified, plugin with that name yield last. """ - for ep in pkg_resources.iter_entry_points(group): + for ep in pkg_resources.iter_entry_points('bitmessage.' + group): if name and ep.name == name or ep.name.startswith(point): try: plugin = ep.load().connect_plugin From de531949e05bc3f59b2bedf83ddde7547ba4246b Mon Sep 17 00:00:00 2001 From: Dmitri Bogomolov <4glitch@gmail.com> Date: Fri, 24 Mar 2017 13:51:46 +0200 Subject: [PATCH 211/407] setup.py changes needed for indicator_libmessaging: - entry point 'indicator' and new extra 'gir' which requires only pygobject - desktop entry - icons are renamed and placed into separate dirs for standard sizes, because data_files keyword not supports file renaming --- setup.py | 27 +++++++++++++++++++++++++-- 1 file changed, 25 insertions(+), 2 deletions(-) diff --git a/setup.py b/setup.py index 06d49ba5..cf6bd9d2 100644 --- a/setup.py +++ b/setup.py @@ -2,6 +2,7 @@ import os import sys +import shutil try: from setuptools import setup, Extension from setuptools.command.install import install @@ -167,6 +168,7 @@ def prereqToPackages(): print packageName[package].get('description') + def compilerToPackages(): if not detectOS() in compiling: return @@ -202,6 +204,14 @@ class InstallCmd(install): except (EOFError, NameError): pass + # prepare icons directories + os.makedirs('desktop/icons/scalable') + shutil.copyfile( + 'desktop/can-icon.svg', 'desktop/icons/scalable/pybitmessage.svg') + os.makedirs('desktop/icons/24x24') + shutil.copyfile( + 'desktop/icon24.png', 'desktop/icons/24x24/pybitmessage.png') + return install.run(self) @@ -253,9 +263,10 @@ if __name__ == "__main__": #keywords='', install_requires=installRequires, extras_require={ + 'gir': ['pygobject'], 'qrcode': ['qrcode'], 'pyopencl': ['pyopencl'], - 'notify2': ['pygobject', 'notify2'], + 'notify2': ['notify2'], 'sound:platform_system=="Windows"': ['winsound'] }, classifiers=[ @@ -273,6 +284,14 @@ if __name__ == "__main__": 'translations/*.ts', 'translations/*.qm', 'images/*.png', 'images/*.ico', 'images/*.icns' ]}, + data_files=[ + ('share/applications/', + ['desktop/pybitmessage.desktop']), + ('share/icons/hicolor/scalable/apps/', + ['desktop/icons/scalable/pybitmessage.svg']), + ('share/icons/hicolor/24x24/apps/', + ['desktop/icons/24x24/pybitmessage.png']) + ], ext_modules=[bitmsghash], zip_safe=False, entry_points={ @@ -282,12 +301,16 @@ if __name__ == "__main__": ], 'bitmessage.notification.message': [ 'notify2 = pybitmessage.plugins.notification_notify2' - '[notify2]' + '[gir, notify2]' ], 'bitmessage.notification.sound': [ 'file.fallback = pybitmessage.plugins.sound_playfile' '[sound]' ], + 'bitmessage.indicator': [ + 'libmessaging =' + 'pybitmessage.plugins.indicator_libmessaging [gir]' + ], # 'console_scripts': [ # 'pybitmessage = pybitmessage.bitmessagemain:main' # ] From 1f47a4060e9bb51d255ab6e3c583e9723d33a295 Mon Sep 17 00:00:00 2001 From: Dmitri Bogomolov <4glitch@gmail.com> Date: Mon, 4 Sep 2017 16:06:48 +0300 Subject: [PATCH 212/407] Added "Set notification sound..." context menu on addressbook entry. --- src/bitmessageqt/__init__.py | 57 +++++++++++++++++++++++++++++++++--- src/bitmessageqt/sound.py | 2 ++ 2 files changed, 55 insertions(+), 4 deletions(-) diff --git a/src/bitmessageqt/__init__.py b/src/bitmessageqt/__init__.py index 942ed3de..bb90bb47 100644 --- a/src/bitmessageqt/__init__.py +++ b/src/bitmessageqt/__init__.py @@ -325,6 +325,10 @@ class MyForm(settingsmixin.SMainWindow): _translate( "MainWindow", "Set avatar..."), self.on_action_AddressBookSetAvatar) + self.actionAddressBookSetSound = \ + self.ui.addressBookContextMenuToolbar.addAction( + _translate("MainWindow", "Set notification sound..."), + self.on_action_AddressBookSetSound) self.actionAddressBookNew = self.ui.addressBookContextMenuToolbar.addAction( _translate( "MainWindow", "Add New Address"), self.on_action_AddressBookNew) @@ -1205,9 +1209,9 @@ class MyForm(settingsmixin.SMainWindow): soundFilename = None def _choose_ext(basename): - for ext in ('.wav', '.mp3', '.oga'): - if os.path.isfile(basename + ext): - return ext + for ext in sound.extensions: + if os.path.isfile(os.extsep.join([basename, ext])): + return os.extsep + ext # if the address had a known label in the address book if label: @@ -3175,6 +3179,7 @@ class MyForm(settingsmixin.SMainWindow): self.popMenuAddressBook.addAction(self.actionAddressBookClipboard) self.popMenuAddressBook.addAction(self.actionAddressBookSubscribe) self.popMenuAddressBook.addAction(self.actionAddressBookSetAvatar) + self.popMenuAddressBook.addAction(self.actionAddressBookSetSound) self.popMenuAddressBook.addSeparator() self.popMenuAddressBook.addAction(self.actionAddressBookNew) normal = True @@ -3578,7 +3583,51 @@ class MyForm(settingsmixin.SMainWindow): return False return True - + + def on_action_AddressBookSetSound(self): + widget = self.ui.tableWidgetAddressBook + self.setAddressSound(widget.item(widget.currentRow(), 0).text()) + + def setAddressSound(self, addr): + filters = [unicode(_translate( + "MainWindow", "Sound files (%s)" % + ' '.join(['*%s%s' % (os.extsep, ext) for ext in sound.extensions]) + ))] + sourcefile = unicode(QtGui.QFileDialog.getOpenFileName( + self, _translate("MainWindow", "Set notification sound..."), + filter=';;'.join(filters) + )) + + if not sourcefile: + return + + destdir = os.path.join(state.appdata, 'sounds') + destfile = unicode(addr) + os.path.splitext(sourcefile)[-1] + destination = os.path.join(destdir, destfile) + + if sourcefile == destination: + return + + pattern = destfile.lower() + for item in os.listdir(destdir): + if item.lower() == pattern: + overwrite = QtGui.QMessageBox.question( + self, _translate("MainWindow", "Message"), + _translate( + "MainWindow", + "You have already set a notification sound" + " for this address book entry." + " Do you really want to overwrite it?"), + QtGui.QMessageBox.Yes, QtGui.QMessageBox.No + ) == QtGui.QMessageBox.Yes + if overwrite: + QtCore.QFile.remove(os.path.join(destdir, item)) + break + + if not QtCore.QFile.copy(sourcefile, destination): + logger.error( + 'couldn\'t copy %s to %s', sourcefile, destination) + def on_context_menuYourIdentities(self, point): currentItem = self.getCurrentItem() self.popMenuYourIdentities = QtGui.QMenu(self) diff --git a/src/bitmessageqt/sound.py b/src/bitmessageqt/sound.py index 4b6aaf00..9c86a9a4 100644 --- a/src/bitmessageqt/sound.py +++ b/src/bitmessageqt/sound.py @@ -17,3 +17,5 @@ def is_connection_sound(category): SOUND_DISCONNECTED, SOUND_CONNECTION_GREEN ) + +extensions = ('wav', 'mp3', 'oga') From 53c3eeb8f77c2d175707d95a5c948df40679a5e8 Mon Sep 17 00:00:00 2001 From: Dmitri Bogomolov <4glitch@gmail.com> Date: Mon, 11 Sep 2017 17:44:17 +0300 Subject: [PATCH 213/407] Sound plugins using pycanberra and gst-python --- setup.py | 3 +++ src/plugins/sound_canberra.py | 21 +++++++++++++++++++++ src/plugins/sound_gstreamer.py | 14 ++++++++++++++ 3 files changed, 38 insertions(+) create mode 100644 src/plugins/sound_canberra.py create mode 100644 src/plugins/sound_gstreamer.py diff --git a/setup.py b/setup.py index cf6bd9d2..dc803931 100644 --- a/setup.py +++ b/setup.py @@ -304,6 +304,9 @@ if __name__ == "__main__": '[gir, notify2]' ], 'bitmessage.notification.sound': [ + 'theme.canberra = pybitmessage.plugins.sound_canberra', + 'file.gstreamer = pybitmessage.plugins.sound_gstreamer' + '[gir]', 'file.fallback = pybitmessage.plugins.sound_playfile' '[sound]' ], diff --git a/src/plugins/sound_canberra.py b/src/plugins/sound_canberra.py new file mode 100644 index 00000000..094901ed --- /dev/null +++ b/src/plugins/sound_canberra.py @@ -0,0 +1,21 @@ +# -*- coding: utf-8 -*- + +from pybitmessage.bitmessageqt import sound + +import pycanberra + +_canberra = pycanberra.Canberra() + +_theme = { + sound.SOUND_UNKNOWN: 'message-new-email', + sound.SOUND_CONNECTED: 'network-connectivity-established', + sound.SOUND_DISCONNECTED: 'network-connectivity-lost', + sound.SOUND_CONNECTION_GREEN: 'network-connectivity-established' +} + + +def connect_plugin(category, label=None): + try: + _canberra.play(0, pycanberra.CA_PROP_EVENT_ID, _theme[category], None) + except (KeyError, pycanberra.CanberraException): + pass diff --git a/src/plugins/sound_gstreamer.py b/src/plugins/sound_gstreamer.py new file mode 100644 index 00000000..062da3f9 --- /dev/null +++ b/src/plugins/sound_gstreamer.py @@ -0,0 +1,14 @@ +# -*- coding: utf-8 -*- + +import gi +gi.require_version('Gst', '1.0') +from gi.repository import Gst # noqa: E402 + +Gst.init(None) +_player = Gst.ElementFactory.make("playbin", "player") + + +def connect_plugin(sound_file): + _player.set_state(Gst.State.NULL) + _player.set_property("uri", "file://" + sound_file) + _player.set_state(Gst.State.PLAYING) From 2504bc66707948cbd8e156b967b953a31336857e Mon Sep 17 00:00:00 2001 From: Dmitri Bogomolov <4glitch@gmail.com> Date: Thu, 21 Sep 2017 13:59:43 +0300 Subject: [PATCH 214/407] Fixed fetch of connected hosts number in bitmessageqt.support --- src/bitmessageqt/support.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/bitmessageqt/support.py b/src/bitmessageqt/support.py index 8ec3c16d..03b302e6 100644 --- a/src/bitmessageqt/support.py +++ b/src/bitmessageqt/support.py @@ -16,7 +16,7 @@ import paths import proofofwork from pyelliptic.openssl import OpenSSL import queues -import shared +import network.stats import state from version import softwareVersion @@ -124,7 +124,7 @@ def createSupportMessage(myapp): upnp = BMConfigParser().get('bitmessagesettings', 'upnp') except: upnp = "N/A" - connectedhosts = len(shared.connectedHostsList) + connectedhosts = len(network.stats.connectedHostsList()) myapp.ui.textEditMessage.setText(str(QtGui.QApplication.translate("Support", SUPPORT_MESSAGE)).format(version, os, architecture, pythonversion, opensslversion, frozen, portablemode, cpow, openclpow, locale, socks, upnp, connectedhosts)) From 196d688b138393d1d540df3322844dfe7e7c02ba Mon Sep 17 00:00:00 2001 From: Peter Surda Date: Thu, 21 Sep 2017 13:43:01 +0200 Subject: [PATCH 215/407] Remove dependency checking from setup.py - still silently checks for msgpack to set the setuptools requirements correctly, but does not display anything --- setup.py | 362 +++++++++++++------------------------------------------ 1 file changed, 83 insertions(+), 279 deletions(-) diff --git a/setup.py b/setup.py index dc803931..44356e7e 100644 --- a/setup.py +++ b/setup.py @@ -3,207 +3,13 @@ import os import sys import shutil -try: - from setuptools import setup, Extension - from setuptools.command.install import install - haveSetuptools = True -except ImportError: - install = object - haveSetuptools = False - -from importlib import import_module +from setuptools import setup, Extension +from setuptools.command.install import install from src.version import softwareVersion -packageManager = { - "OpenBSD": "pkg_add", - "FreeBSD": "pkg install", - "Debian": "apt-get install", - "Ubuntu": "apt-get install", - "Ubuntu 12": "apt-get install", - "openSUSE": "zypper install", - "Fedora": "dnf install", - "Guix": "guix package -i", - "Gentoo": "emerge" -} - -packageName = { - "PyQt4": { - "OpenBSD": "py-qt4", - "FreeBSD": "py27-qt4", - "Debian": "python-qt4", - "Ubuntu": "python-qt4", - "Ubuntu 12": "python-qt4", - "openSUSE": "python-qt", - "Fedora": "PyQt4", - "Guix": "python2-pyqt@4.11.4", - "Gentoo": "dev-python/PyQt4", - 'optional': True, - 'description': "You only need PyQt if you want to use the GUI. " \ - "When only running as a daemon, this can be skipped.\n" \ - "However, you would have to install it manually " \ - "because setuptools does not support PyQt." - }, - "msgpack": { - "OpenBSD": "py-msgpack", - "FreeBSD": "py27-msgpack-python", - "Debian": "python-msgpack", - "Ubuntu": "python-msgpack", - "Ubuntu 12": "msgpack-python", - "openSUSE": "python-msgpack-python", - "Fedora": "python2-msgpack", - "Guix": "python2-msgpack", - "Gentoo": "dev-python/msgpack", - "optional": True, - "description": "python-msgpack is recommended for messages coding" - }, - "umsgpack": { - "FreeBSD": "", - "OpenBSD": "", - "Fedora": "", - "openSUSE": "", - "Guix": "", - "Ubuntu 12": "", - "Debian": "python-u-msgpack", - "Ubuntu": "python-u-msgpack", - "Gentoo": "dev-python/u-msgpack", - "optional": True, - "description": "umsgpack can be used instead of msgpack" - }, - "pyopencl": { - "FreeBSD": "py27-pyopencl", - "Debian": "python-pyopencl", - "Ubuntu": "python-pyopencl", - "Ubuntu 12": "python-pyopencl", - "Fedora": "python2-pyopencl", - "openSUSE": "", - "OpenBSD": "", - "Guix": "", - "Gentoo": "dev-python/pyopencl", - "optional": True, - 'description': "If you install pyopencl, you will be able to use " \ - "GPU acceleration for proof of work. \n" \ - "You also need a compatible GPU and drivers." - }, - "setuptools": { - "OpenBSD": "py-setuptools", - "FreeBSD": "py27-setuptools", - "Debian": "python-setuptools", - "Ubuntu": "python-setuptools", - "Ubuntu 12": "python-setuptools", - "Fedora": "python2-setuptools", - "openSUSE": "python-setuptools", - "Guix": "python2-setuptools", - "Gentoo": "", - } -} - -compiling = { - "Debian": "build-essential libssl-dev", - "Ubuntu": "build-essential libssl-dev", - "Fedora": "gcc-c++ redhat-rpm-config python-devel openssl-devel", - "openSUSE": "gcc-c++ libopenssl-devel python-devel", -} - - -def detectOS(): - if detectOS.result is not None: - return detectOS.result - if sys.platform.startswith('openbsd'): - detectOS.result = "OpenBSD" - elif sys.platform.startswith('freebsd'): - detectOS.result = "FreeBSD" - elif sys.platform.startswith('win'): - detectOS.result = "Windows" - elif os.path.isfile("/etc/os-release"): - with open("/etc/os-release", 'r') as osRelease: - version = None - for line in osRelease: - if line.startswith("NAME="): - line = line.lower() - if "fedora" in line: - detectOS.result = "Fedora" - elif "opensuse" in line: - detectOS.result = "openSUSE" - elif "ubuntu" in line: - detectOS.result = "Ubuntu" - elif "debian" in line: - detectOS.result = "Debian" - elif "gentoo" in line or "calculate" in line: - detectOS.result = "Gentoo" - else: - detectOS.result = None - if line.startswith("VERSION_ID="): - try: - version = float(line.split("\"")[1]) - except ValueError: - pass - if detectOS.result == "Ubuntu" and version < 14: - detectOS.result = "Ubuntu 12" - elif os.path.isfile("/etc/config.scm"): - detectOS.result = "Guix" - return detectOS.result - - -def detectPrereqs(missing=True): - available = [] - for module in packageName.keys(): - try: - import_module(module) - if not missing: - available.append(module) - except ImportError: - if missing: - available.append(module) - return available - - -def prereqToPackages(): - print "You can install the requirements by running, as root:" - print "%s %s" % ( - packageManager[detectOS()], " ".join( - packageName[x][detectOS()] for x in detectPrereqs())) - for package in detectPrereqs(): - if packageName[package].get('optional'): - print packageName[package].get('description') - - - -def compilerToPackages(): - if not detectOS() in compiling: - return - print "You can install the requirements by running, as root:" - print "%s %s" % ( - packageManager[detectOS.result], compiling[detectOS.result]) - - class InstallCmd(install): def run(self): - detectOS.result = None - prereqs = detectPrereqs() - if prereqs and detectOS() in packageManager: - print "It looks like you're using %s. " \ - "It is highly recommended to use the package manager " \ - "instead of setuptools." % (detectOS.result) - prereqToPackages() - try: - for module in prereqs: - if not packageName[module]['optional']: - sys.exit() - except KeyError: - sys.exit() - - if not haveSetuptools: - print "It looks like you're missing setuptools." - sys.exit() - - if prereqs and sys.stdin.isatty(): - print "Press Return to continue" - try: - raw_input() - except (EOFError, NameError): - pass - # prepare icons directories os.makedirs('desktop/icons/scalable') shutil.copyfile( @@ -238,92 +44,90 @@ if __name__ == "__main__": 'pybitmessage.storage', 'pybitmessage.plugins' ] + # this will silently accept alternative providers of msgpack # if they are already installed - if "msgpack" in detectPrereqs(False): - installRequires.append("msgpack-python") - elif "umsgpack" in detectPrereqs(False): - installRequires.append("umsgpack") - else: - packages += ['pybitmessage.fallback', 'pybitmessage.fallback.umsgpack'] try: - dist = setup( - name='pybitmessage', - version=softwareVersion, - description="Reference client for Bitmessage: " - "a P2P communications protocol", - long_description=README, - license='MIT', - # TODO: add author info - #author='', - #author_email='', - url='https://bitmessage.org', - # TODO: add keywords - #keywords='', - install_requires=installRequires, - extras_require={ - 'gir': ['pygobject'], - 'qrcode': ['qrcode'], - 'pyopencl': ['pyopencl'], - 'notify2': ['notify2'], - 'sound:platform_system=="Windows"': ['winsound'] - }, - classifiers=[ - "License :: OSI Approved :: MIT License" - "Operating System :: OS Independent", - "Programming Language :: Python :: 2.7 :: Only", - "Topic :: Internet", - "Topic :: Security :: Cryptography", - "Topic :: Software Development :: Libraries :: Python Modules", + import msgpack + installRequires.append("msgpack-python") + except ImportError: + try: + import umsgpack + installRequires.append("umsgpack") + except ImportError: + packages += ['pybitmessage.fallback', 'pybitmessage.fallback.umsgpack'] + + dist = setup( + name='pybitmessage', + version=softwareVersion, + description="Reference client for Bitmessage: " + "a P2P communications protocol", + long_description=README, + license='MIT', + # TODO: add author info + #author='', + #author_email='', + url='https://bitmessage.org', + # TODO: add keywords + #keywords='', + install_requires=installRequires, + extras_require={ + 'gir': ['pygobject'], + 'qrcode': ['qrcode'], + 'pyopencl': ['pyopencl'], + 'notify2': ['notify2'], + 'sound:platform_system=="Windows"': ['winsound'] + }, + classifiers=[ + "License :: OSI Approved :: MIT License" + "Operating System :: OS Independent", + "Programming Language :: Python :: 2.7 :: Only", + "Topic :: Internet", + "Topic :: Security :: Cryptography", + "Topic :: Software Development :: Libraries :: Python Modules", + ], + package_dir={'pybitmessage': 'src'}, + packages=packages, + package_data={'': [ + 'bitmessageqt/*.ui', 'bitmsghash/*.cl', 'sslkeys/*.pem', + 'translations/*.ts', 'translations/*.qm', + 'images/*.png', 'images/*.ico', 'images/*.icns' + ]}, + data_files=[ + ('share/applications/', + ['desktop/pybitmessage.desktop']), + ('share/icons/hicolor/scalable/apps/', + ['desktop/icons/scalable/pybitmessage.svg']), + ('share/icons/hicolor/24x24/apps/', + ['desktop/icons/24x24/pybitmessage.png']) + ], + ext_modules=[bitmsghash], + zip_safe=False, + entry_points={ + 'bitmessage.gui.menu': [ + 'popMenuYourIdentities.qrcode = ' + 'pybitmessage.plugins.qrcodeui [qrcode]' ], - package_dir={'pybitmessage': 'src'}, - packages=packages, - package_data={'': [ - 'bitmessageqt/*.ui', 'bitmsghash/*.cl', 'sslkeys/*.pem', - 'translations/*.ts', 'translations/*.qm', - 'images/*.png', 'images/*.ico', 'images/*.icns' - ]}, - data_files=[ - ('share/applications/', - ['desktop/pybitmessage.desktop']), - ('share/icons/hicolor/scalable/apps/', - ['desktop/icons/scalable/pybitmessage.svg']), - ('share/icons/hicolor/24x24/apps/', - ['desktop/icons/24x24/pybitmessage.png']) + 'bitmessage.notification.message': [ + 'notify2 = pybitmessage.plugins.notification_notify2' + '[gir, notify2]' ], - ext_modules=[bitmsghash], - zip_safe=False, - entry_points={ - 'bitmessage.gui.menu': [ - 'popMenuYourIdentities.qrcode = ' - 'pybitmessage.plugins.qrcodeui [qrcode]' - ], - 'bitmessage.notification.message': [ - 'notify2 = pybitmessage.plugins.notification_notify2' - '[gir, notify2]' - ], - 'bitmessage.notification.sound': [ - 'theme.canberra = pybitmessage.plugins.sound_canberra', - 'file.gstreamer = pybitmessage.plugins.sound_gstreamer' - '[gir]', - 'file.fallback = pybitmessage.plugins.sound_playfile' - '[sound]' - ], - 'bitmessage.indicator': [ - 'libmessaging =' - 'pybitmessage.plugins.indicator_libmessaging [gir]' - ], - # 'console_scripts': [ - # 'pybitmessage = pybitmessage.bitmessagemain:main' - # ] - }, - scripts=['src/pybitmessage'], - cmdclass={'install': InstallCmd} - ) - except SystemExit as err: - print err.message - except: - print "It looks like building the package failed.\n" \ - "You may be missing a C++ compiler and the OpenSSL headers." - compilerToPackages() + 'bitmessage.notification.sound': [ + 'theme.canberra = pybitmessage.plugins.sound_canberra', + 'file.gstreamer = pybitmessage.plugins.sound_gstreamer' + '[gir]', + 'file.fallback = pybitmessage.plugins.sound_playfile' + '[sound]' + ], + 'bitmessage.indicator': [ + 'libmessaging =' + 'pybitmessage.plugins.indicator_libmessaging [gir]' + ], + # 'console_scripts': [ + # 'pybitmessage = pybitmessage.bitmessagemain:main' + # ] + }, + scripts=['src/pybitmessage'], + cmdclass={'install': InstallCmd} + ) From 0150a35dd44fd1a6a2b934c700800e24e3636499 Mon Sep 17 00:00:00 2001 From: Peter Surda Date: Thu, 21 Sep 2017 17:51:34 +0200 Subject: [PATCH 216/407] Change main thread name to PyBitmessage --- dev/powinterrupttest.py | 2 +- src/bitmessagemain.py | 2 +- src/helper_generic.py | 2 +- src/helper_threading.py | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/dev/powinterrupttest.py b/dev/powinterrupttest.py index 2f8cfcac..cc4c2197 100644 --- a/dev/powinterrupttest.py +++ b/dev/powinterrupttest.py @@ -14,7 +14,7 @@ def signal_handler(signal, frame): print "Got signal %i in %s/%s" % (signal, current_process().name, current_thread().name) if current_process().name != "MainProcess": raise StopIteration("Interrupted") - if current_thread().name != "MainThread": + if current_thread().name != "PyBitmessage": return shutdown = 1 diff --git a/src/bitmessagemain.py b/src/bitmessagemain.py index 1d37123f..9182b4cb 100755 --- a/src/bitmessagemain.py +++ b/src/bitmessagemain.py @@ -215,7 +215,7 @@ class Main: self.setSignalHandler() - helper_threading.set_thread_name("MainThread") + helper_threading.set_thread_name("PyBitmessage") helper_bootstrap.knownNodes() # Start the address generation thread diff --git a/src/helper_generic.py b/src/helper_generic.py index 26d65dcb..b750e519 100644 --- a/src/helper_generic.py +++ b/src/helper_generic.py @@ -51,7 +51,7 @@ def signal_handler(signal, frame): raise SystemExit if "PoolWorker" in current_process().name: raise SystemExit - if current_thread().name != "MainThread": + if current_thread().name != "PyBitmessage": return logger.error("Got signal %i", signal) if BMConfigParser().safeGetBoolean('bitmessagesettings', 'daemon'): diff --git a/src/helper_threading.py b/src/helper_threading.py index 56c5b8b2..3b7ba378 100644 --- a/src/helper_threading.py +++ b/src/helper_threading.py @@ -12,7 +12,7 @@ try: threading.Thread.__bootstrap_original__ = threading.Thread._Thread__bootstrap threading.Thread._Thread__bootstrap = _thread_name_hack except ImportError: - def set_thread_name(name): pass + def set_thread_name(name): threading.current_thread().name = name class StoppableThread(object): def initStop(self): From 1881bcea6848be24b8f30172fd91070eaf8e174c Mon Sep 17 00:00:00 2001 From: Peter Surda Date: Thu, 21 Sep 2017 18:18:42 +0200 Subject: [PATCH 217/407] Don't connect on first start until approved --- src/network/connectionpool.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/network/connectionpool.py b/src/network/connectionpool.py index 1026d305..3a7f9e6d 100644 --- a/src/network/connectionpool.py +++ b/src/network/connectionpool.py @@ -141,9 +141,7 @@ class BMConnectionPool(object): acceptConnections = True if BMConfigParser().safeGetBoolean('bitmessagesettings', 'dontconnect'): acceptConnections = False - else: - spawnConnections = True - if BMConfigParser().safeGetBoolean('bitmessagesettings', 'sendoutgoingconnections'): + elif BMConfigParser().safeGetBoolean('bitmessagesettings', 'sendoutgoingconnections'): spawnConnections = True if BMConfigParser().get('bitmessagesettings', 'socksproxytype')[0:5] == 'SOCKS' and \ (not BMConfigParser().getboolean('bitmessagesettings', 'sockslisten') and \ From df21f53271af10b6a7f56408112cd644bddaf6e8 Mon Sep 17 00:00:00 2001 From: PyBitmessage Translations Date: Thu, 21 Sep 2017 19:08:10 +0200 Subject: [PATCH 218/407] Auto-updated language eo from transifex --- src/translations/bitmessage_eo.qm | Bin 88118 -> 88527 bytes src/translations/bitmessage_eo.ts | 323 ++++++++++++++++-------------- 2 files changed, 169 insertions(+), 154 deletions(-) diff --git a/src/translations/bitmessage_eo.qm b/src/translations/bitmessage_eo.qm index e2ac21b2aa70225463a742effbc36a26f4194b0b..018a268ec83772d69447e5484473f13f7826df76 100644 GIT binary patch delta 3567 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 VMCroyaWM&|oo8K2Zv7aR_dic^H1hxe delta 3371 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 EmailGatewayRegistrationDialog - + Registration failed: Registrado malsukcesis: - + The requested email address is not available, please try a new one. Fill out the new desired email address (including @mailchuck.com) below: La dezirata retpoŝtadreso ne estas disponebla, bonvolu provi alian. Entajpu novan deziratan adreson (kune kun @mailchuck.com) sube: @@ -166,52 +166,52 @@ Bonvolu entajpi deziratan retpoŝtadreson (kune kun @mailchuck.com) sube: MainWindow - + Reply to sender Respondi al sendinto - + Reply to channel Respondi al kanalo - + Add sender to your Address Book Aldoni sendinton al via adresaro - + Add sender to your Blacklist Aldoni sendinton al via nigra listo - + Move to Trash Movi al rubujo - + Undelete Malforviŝi - + View HTML code as formatted text Montri HTML-n kiel aranĝitan tekston - + Save message as... Konservi mesaĝon kiel… - + Mark Unread Marki kiel nelegitan - + New Nova @@ -236,12 +236,12 @@ Bonvolu entajpi deziratan retpoŝtadreson (kune kun @mailchuck.com) sube:Kopii adreson al tondejo - + Special address behavior... Speciala sinteno de adreso… - + Email gateway Retpoŝta kluzo @@ -251,37 +251,37 @@ Bonvolu entajpi deziratan retpoŝtadreson (kune kun @mailchuck.com) sube:Forigi - + Send message to this address Sendi mesaĝon al tiu adreso - + Subscribe to this address Aboni tiun adreson - + Add New Address Aldoni novan adreson - + Copy destination address to clipboard Kopii cel-adreson al tondejo - + Force send Devigi sendadon - + One of your addresses, %1, is an old version 1 address. Version 1 addresses are no longer supported. May we delete it now? Iu de viaj adresoj, %1, estas malnova versio 1 adreso. Versioj 1 adresoj ne estas jam subtenataj. Ĉu ni povas forigi ĝin? - + Waiting for their encryption key. Will request it again soon. Atendado je ilia ĉifroŝlosilo. Baldaŭ petos ĝin denove. @@ -291,17 +291,17 @@ Bonvolu entajpi deziratan retpoŝtadreson (kune kun @mailchuck.com) sube: - + Queued. En atendovico. - + Message sent. Waiting for acknowledgement. Sent at %1 Mesaĝo sendita. Atendado je konfirmo. Sendita je %1 - + Message sent. Sent at %1 Mesaĝo sendita. Sendita je %1 @@ -311,47 +311,47 @@ Bonvolu entajpi deziratan retpoŝtadreson (kune kun @mailchuck.com) sube: - + Acknowledgement of the message received %1 Ricevis konfirmon de la mesaĝo je %1 - + Broadcast queued. Elsendo en atendovico. - + Broadcast on %1 Elsendo je %1 - + Problem: The work demanded by the recipient is more difficult than you are willing to do. %1 Problemo: la demandita laboro de la ricevonto estas pli malfacila ol vi pretas fari. %1 - + Problem: The recipient's encryption key is no good. Could not encrypt message. %1 Problemo: la ĉifroŝlosilo de la ricevonto estas rompita. Ne povis ĉifri la mesaĝon. %1 - + Forced difficulty override. Send should start soon. Devigita superado de limito de malfacilaĵo. Sendado devus baldaŭ komenci. - + Unknown status: %1 %2 Nekonata stato: %1 %2 - + Not Connected Ne konektita - + Show Bitmessage Montri Bitmesaĝon @@ -361,12 +361,12 @@ Bonvolu entajpi deziratan retpoŝtadreson (kune kun @mailchuck.com) sube:Sendi - + Subscribe Aboni - + Channel Kanalo @@ -376,12 +376,12 @@ Bonvolu entajpi deziratan retpoŝtadreson (kune kun @mailchuck.com) sube:Eliri - + You may manage your keys by editing the keys.dat file stored in the same directory as this program. It is important that you back up this file. Vi povas administri viajn ŝlosilojn per redakti la dosieron keys.dat en la sama dosierujo kiel tiu programo. Estas grava, ke vi faru sekurkopion de tiu dosiero. - + You may manage your keys by editing the keys.dat file stored in %1 It is important that you back up this file. @@ -390,17 +390,17 @@ It is important that you back up this file. Estas grava, ke vi faru sekurkopion de tiu dosiero. - + Open keys.dat? Ĉu malfermi keys.dat? - + You may manage your keys by editing the keys.dat file stored in the same directory as this program. It is important that you back up this file. Would you like to open the file now? (Be sure to close Bitmessage before making any changes.) Vi povas administri viajn ŝlosilojn per redakti la dosieron keys.dat en la sama dosierujo kiel tiu programo. Estas grava ke vi faru sekurkopion de tiu dosiero. Ĉu vi volas malfermi la dosieron nun? (Bonvolu certigi ke Bitmesaĝo estas fermita antaŭ fari ŝanĝojn.) - + You may manage your keys by editing the keys.dat file stored in %1 It is important that you back up this file. Would you like to open the file now? (Be sure to close Bitmessage before making any changes.) @@ -409,37 +409,37 @@ It is important that you back up this file. Would you like to open the file now? Estas grava, ke vi faru sekurkopion de tiu dosiero. Ĉu vi volas malfermi la dosieron nun? (Bonvolu certigi ke Bitmesaĝo estas fermita antaŭ fari ŝanĝojn.) - + Delete trash? Ĉu malplenigi rubujon? - + Are you sure you want to delete all trashed messages? Ĉu vi certe volas forviŝi ĉiujn mesaĝojn el la rubujo? - + bad passphrase malprava pasvorto - + You must type your passphrase. If you don't have one then this is not the form for you. Vi devas tajpi vian pasvorton. Se vi ne havas pasvorton, tiu ĉi ne estas la prava formularo por vi. - + Bad address version number Erara numero de adresversio - + Your address version number must be a number: either 3 or 4. Via numero de adresversio devas esti: aŭ 3 aŭ 4. - + Your address version number must be either 3 or 4. Via numero de adresversio devas esti: aŭ 3 aŭ 4. @@ -509,22 +509,22 @@ Estas grava, ke vi faru sekurkopion de tiu dosiero. Ĉu vi volas malfermi la dos - + Connection lost Perdis konekton - + Connected Konektita - + Message trashed Movis mesaĝon al rubujo - + The TTL, or Time-To-Live is the length of time that the network will hold the message. The recipient must get it during this time. If your Bitmessage client does not hear an acknowledgement, it will resend the message automatically. The longer the Time-To-Live, the @@ -532,17 +532,17 @@ Estas grava, ke vi faru sekurkopion de tiu dosiero. Ĉu vi volas malfermi la dos La vivdaŭro signifas ĝis kiam la reto tenos la mesaĝon. La ricevonto devos elŝuti ĝin dum tiu tempo. Se via bitmesaĝa kliento ne ricevos konfirmon, ĝi resendos mesaĝon aŭtomate. Ju pli longa vivdaŭro, des pli laboron via komputilo bezonos fari por sendi mesaĝon. Vivdaŭro proksimume kvin aŭ kvar horoj estas ofte konvena. - + Message too long Mesaĝo tro longa - + The message that you are trying to send is too long by %1 bytes. (The maximum is 261644 bytes). Please cut it down before sending. La mesaĝon kiun vi provis sendi estas tro longa je %1 bitokoj. (La maksimumo estas 261644 bitokoj.) Bonvolu mallongigi ĝin antaŭ sendado. - + Error: Your account wasn't registered at an email gateway. Sending registration now as %1, please wait for the registration to be processed before retrying sending. Eraro: Via konto ne estas registrita je retpoŝta kluzo. Registranta nun kiel %1, bonvolu atendi ĝis la registrado finos antaŭ vi reprovos sendi iun ajn. @@ -587,67 +587,67 @@ Estas grava, ke vi faru sekurkopion de tiu dosiero. Ĉu vi volas malfermi la dos - + Error: You must specify a From address. If you don't have one, go to the 'Your Identities' tab. Eraro: Vi devas elekti sendontan adreson. Se vi ne havas iun, iru al langeto 'Viaj identigoj'. - + Address version number Numero de adresversio - + Concerning the address %1, Bitmessage cannot understand address version numbers of %2. Perhaps upgrade Bitmessage to the latest version. Dum prilaborado de adreso adreso %1, Bitmesaĝo ne povas kompreni numerojn %2 de adresversioj. Eble ĝisdatigu Bitmesaĝon al la plej nova versio. - + Stream number Fluo numero - + Concerning the address %1, Bitmessage cannot handle stream numbers of %2. Perhaps upgrade Bitmessage to the latest version. Dum prilaborado de adreso %1, Bitmesaĝo ne povas priservi %2 fluojn numerojn. Eble ĝisdatigu Bitmesaĝon al la plej nova versio. - + Warning: You are currently not connected. Bitmessage will do the work necessary to send the message but it won't send until you connect. Atentu: Vi ne estas nun konektita. Bitmesaĝo faros necesan laboron por sendi mesaĝon, tamen ĝi ne sendos ĝin antaŭ vi konektos. - + Message queued. Mesaĝo envicigita. - + Your 'To' field is empty. Via "Ricevonto"-kampo malplenas. - + Right click one or more entries in your address book and select 'Send message to this address'. Dekstre alklaku kelka(j)n elemento(j)n en via adresaro kaj elektu 'Sendi mesaĝon al tiu adreso'. - + Fetched address from namecoin identity. Venigis adreson de namecoin-a identigo. - + New Message Nova mesaĝo - + From - De + - + Sending email gateway registration request Sendado de peto pri registrado je retpoŝta kluzo @@ -662,142 +662,142 @@ Estas grava, ke vi faru sekurkopion de tiu dosiero. Ĉu vi volas malfermi la dos La adreso kiun vi enmetis estas malĝusta. Ignoras ĝin. - + Error: You cannot add the same address to your address book twice. Try renaming the existing one if you want. Eraro: Vi ne povas duoble aldoni la saman adreson al via adresaro. Provu renomi la ekzistan se vi volas. - + Error: You cannot add the same address to your subscriptions twice. Perhaps rename the existing one if you want. Eraro: Vi ne povas aldoni duoble la saman adreson al viaj abonoj. Eble renomi la ekzistan se vi volas. - + Restart Restartigi - + You must restart Bitmessage for the port number change to take effect. Vi devas restartigi Bitmesaĝon por ke la ŝanĝo de la numero de pordo (Port Number) efektivigu. - + Bitmessage will use your proxy from now on but you may want to manually restart Bitmessage now to close existing connections (if any). Bitmesaĝo uzos vian prokurilon (proxy) ekde nun, sed eble vi volas permane restartigi Bitmesaĝon nun, por ke ĝi fermu eblajn ekzistajn konektojn. - + Number needed Numero bezonata - + Your maximum download and upload rate must be numbers. Ignoring what you typed. Maksimumaj elŝutrapido kaj alŝutrapido devas esti numeroj. Ignoras kion vi enmetis. - + Will not resend ever Resendos neniam - + Note that the time limit you entered is less than the amount of time Bitmessage waits for the first resend attempt therefore your messages will never be resent. Rigardu, ke la templimon vi enmetis estas pli malgrandan ol tempo dum kiu Bitmesaĝo atendas por resendi unuafoje, do viaj mesaĝoj estos senditaj neniam. - + Sending email gateway unregistration request Sendado de peto pri malregistrado de retpoŝta kluzo - + Sending email gateway status request Sendado de peto pri stato de retpoŝta kluzo - + Passphrase mismatch Pasfrazoj malsamas - + The passphrase you entered twice doesn't match. Try again. La pasfrazo kiun vi duoble enmetis malsamas. Provu denove. - + Choose a passphrase Elektu pasfrazon - + You really do need a passphrase. Vi ja vere bezonas pasfrazon. - + Address is gone Adreso foriris - + Bitmessage cannot find your address %1. Perhaps you removed it? Bitmesaĝo ne povas trovi vian adreson %1. Ĉu eble vi forviŝis ĝin? - + Address disabled Adreso malŝaltita - + Error: The address from which you are trying to send is disabled. You'll have to enable it on the 'Your Identities' tab before using it. Eraro: La adreso kun kiu vi provas sendi estas malŝaltita. Vi devos ĝin ŝalti en la langeto 'Viaj identigoj' antaŭ uzi ĝin. - + Entry added to the Address Book. Edit the label to your liking. Aldonis elementon al adresaro. Redaktu la etikedon laŭvole. - + Entry added to the blacklist. Edit the label to your liking. Aldonis elementon al la nigra listo. Redaktu la etikedon laŭvole. - + Error: You cannot add the same address to your blacklist twice. Try renaming the existing one if you want. Eraro: Vi ne povas duoble aldoni la saman adreson al via nigra listo. Provu renomi la jaman se vi volas. - + Moved items to trash. Movis elementojn al rubujo. - + Undeleted item. Malforviŝis elementon. - + Save As... Konservi kiel… - + Write error. Skriberaro. - + No addresses selected. Neniu adreso elektita. - + If you delete the subscription, messages that you already received will become inaccessible. Maybe you can consider disabling the subscription instead. Disabled subscriptions will not receive new messages, but you can still view messages you already received. Are you sure you want to delete the subscription? @@ -806,7 +806,7 @@ Are you sure you want to delete the subscription? Ĉu vi certe volas forviŝi la abonon? - + If you delete the channel, messages that you already received will become inaccessible. Maybe you can consider disabling the channel instead. Disabled channels will not receive new messages, but you can still view messages you already received. Are you sure you want to delete the channel? @@ -815,92 +815,92 @@ Are you sure you want to delete the channel? Ĉu vi certe volas forviŝi la kanalon? - + Do you really want to remove this avatar? Ĉu vi certe volas forviŝi tiun ĉi avataron? - + You have already set an avatar for this address. Do you really want to overwrite it? Vi jam agordis avataron por tiu ĉi adreso. Ĉu vi vere volas superskribi ĝin? - + Start-on-login not yet supported on your OS. Starto-dum-ensaluto ne estas ankoraŭ ebla en via operaciumo. - + Minimize-to-tray not yet supported on your OS. Plejetigo al taskopleto ne estas ankoraŭ ebla en via operaciumo. - + Tray notifications not yet supported on your OS. Taskopletaj sciigoj ne estas ankoraŭ eblaj en via operaciumo. - + Testing... Testado… - + This is a chan address. You cannot use it as a pseudo-mailing list. Tio ĉi estas kanaladreso. Vi ne povas ĝin uzi kiel kvazaŭ-dissendolisto. - + The address should start with ''BM-'' La adreso komencu kun "BM-" - + The address is not typed or copied correctly (the checksum failed). La adreso ne estis prave tajpita aŭ kopiita (kontrolsumo malsukcesis). - + The version number of this address is higher than this software can support. Please upgrade Bitmessage. La numero de adresversio estas pli alta ol tiun, kiun la programo poveblas subteni. Bonvolu ĝisdatigi Bitmesaĝon. - + The address contains invalid characters. La adreso enhavas malpermesitajn simbolojn. - + Some data encoded in the address is too short. Kelkaj datumoj koditaj en la adreso estas tro mallongaj. - + Some data encoded in the address is too long. Kelkaj datumoj koditaj en la adreso estas tro longaj. - + Some data encoded in the address is malformed. Kelkaj datumoj koditaj en la adreso estas misformitaj. - + Enter an address above. Enmetu adreson supre. - + Address is an old type. We cannot display its past broadcasts. Malnova speco de adreso. Ne povas montri ĝiajn antaŭajn elsendojn. - + There are no recent broadcasts from this address to display. Neniaj lastatempaj elsendoj de tiu ĉi adreso por montri. - + You are using TCP port %1. (This can be changed in the settings). Vi uzas TCP-pordon %1 (tio ĉi estas ŝanĝebla en la agordoj). @@ -1110,47 +1110,47 @@ Are you sure you want to delete the channel? Aldoni novan elementon - + Display the %1 recent broadcast(s) from this address. Montri la %1 lasta(j)n elsendo(j)n de tiu adreso. - + New version of PyBitmessage is available: %1. Download it from https://github.com/Bitmessage/PyBitmessage/releases/latest La nova versio de PyBitmessage estas disponebla: %1. Elŝutu ĝin de https://github.com/Bitmessage/PyBitmessage/releases/latest - + Waiting for PoW to finish... %1% Atendado ĝis laborpruvo finiĝos… %1% - + Shutting down Pybitmessage... %1% Fermado de PyBitmessage… %1% - + Waiting for objects to be sent... %1% Atendado ĝis objektoj estos senditaj… %1% - + Saving settings... %1% Konservado de agordoj… %1% - + Shutting down core... %1% Fermado de kerno… %1% - + Stopping notifications... %1% Haltigado de sciigoj… %1% - + Shutdown imminent... %1% Fermado tuj… %1% @@ -1160,17 +1160,17 @@ Are you sure you want to delete the channel? %n horo%n horoj - + %n day(s) %n tago%n tagoj - + Shutting down PyBitmessage... %1% Fermado de PyBitmessage… %1% - + Sent Senditaj @@ -1274,7 +1274,7 @@ Ricevonto postulas malfacilaĵon: %1 kaj %2 Eraro: Vi provis sendi mesaĝon al vi mem aŭ al kanalo, tamen via ĉifroŝlosilo ne estas trovebla en la dosiero keys.dat. Mesaĝo ne povis esti ĉifrita. %1 - + Doing work necessary to send message. Kalkulado de laborpruvo, kiu endas por sendi mesaĝon. @@ -1284,7 +1284,7 @@ Ricevonto postulas malfacilaĵon: %1 kaj %2 Mesaĝo sendita. Atendado je konfirmo. Sendita je %1 - + Doing work necessary to request encryption key. Kalkulado de laborpruvo, kiu endas por peti pri ĉifroŝlosilo. @@ -1309,37 +1309,37 @@ Ricevonto postulas malfacilaĵon: %1 kaj %2 UPnP pord-mapigo forigita - + Mark all messages as read Marki ĉiujn mesaĝojn kiel legitajn - + Are you sure you would like to mark all messages read? Ĉu vi certe volas marki ĉiujn mesaĝojn kiel legitajn? - + Doing work necessary to send broadcast. Kalkulado de laborpruvo, kiu endas por sendi elsendon. - + Proof of work pending Laborpruvo haltigita - + %n object(s) pending proof of work Haltigis laborpruvon por %n objektoHaltigis laborpruvon por %n objektoj - + %n object(s) waiting to be distributed %n objekto atendas je sendato%n objektoj atendas je sendato - + Wait until these tasks finish? Ĉu atendi ĝis tiujn taskojn finos? @@ -1404,7 +1404,12 @@ Ricevonto postulas malfacilaĵon: %1 kaj %2 Via(j) vidprocesoro(j) ne kalkulis senerare, malaktiviganta OpenCL. Bonvolu raporti tion al programistoj. - + + Set notification sound... + Agordi sciigan sonon… + + + Welcome to easy and secure Bitmessage * send messages to other people @@ -1418,90 +1423,100 @@ Bonvenon al facila kaj sekura Bitmesaĝo * babili kun aliaj uloj en mesaĝ-kanaloj - + not recommended for chans malkonsilinda por kanaloj - + Problems connecting? Try enabling UPnP in the Network Settings Ĉu problemo kun konektado? Provu aktivigi UPnP en retaj agordoj. - + You are trying to send an email instead of a bitmessage. This requires registering with a gateway. Attempt to register? Vi provas sendi retmesaĝon anstataŭ bitmesaĝ-mesaĝon. Tio ĉi postulas registri ĉe retpoŝta kluzo. Ĉu provi registri? - + Error: Bitmessage addresses start with BM- Please check the recipient address %1 Eraro: bitmesaĝaj adresoj komenciĝas kun BM-. Bonvolu kontroli la adreson de ricevonto %1 - + Error: The recipient address %1 is not typed or copied correctly. Please check it. Eraro: la adreso de ricevonto %1 estas malprave tajpita aŭ kopiita. Bonvolu kontroli ĝin. - + Error: The recipient address %1 contains invalid characters. Please check it. Eraro: la adreso de ricevonto %1 enhavas malpermesatajn simbolojn. Bonvolu kontroli ĝin. - + Error: The version of the recipient address %1 is too high. Either you need to upgrade your Bitmessage software or your acquaintance is being clever. Eraro: la versio de adreso de ricevonto %1 estas tro alta. Eble vi devas ĝisdatigi vian bitmesaĝan programon aŭ via sagaca konato uzas alian programon. - + Error: Some data encoded in the recipient address %1 is too short. There might be something wrong with the software of your acquaintance. Eraro: kelkaj datumoj koditaj en la adreso de ricevonto %1 estas tro mallongaj. Povus esti ke io en la programo de via konato malfunkcias. - + Error: Some data encoded in the recipient address %1 is too long. There might be something wrong with the software of your acquaintance. Eraro: kelkaj datumoj koditaj en la adreso de ricevonto %1 estas tro longaj. Povus esti ke io en la programo de via konato malfunkcias. - + Error: Some data encoded in the recipient address %1 is malformed. There might be something wrong with the software of your acquaintance. Eraro: kelkaj datumoj koditaj en la adreso de ricevonto %1 estas misformitaj. Povus esti ke io en la programo de via konato malfunkcias. - + Error: Something is wrong with the recipient address %1. Eraro: io malĝustas kun la adreso de ricevonto %1. - + + From %1 + De %1 + + + Synchronisation pending Samtempigado haltigita - + Bitmessage hasn't synchronised with the network, %n object(s) to be downloaded. If you quit now, it may cause delivery delays. Wait until the synchronisation finishes? Bitmesaĝo ne estas samtempigita kun la reto, %n objekto elŝutendas. Se vi eliros nun, tio povas igi malfruiĝojn de liveradoj. Ĉu atendi ĝis la samtempigado finiĝos?Bitmesaĝo ne estas samtempigita kun la reto, %n objektoj elŝutendas. Se vi eliros nun, tio povas igi malfruiĝojn de liveradoj. Ĉu atendi ĝis la samtempigado finiĝos? - + Not connected Nekonektita - + Bitmessage isn't connected to the network. If you quit now, it may cause delivery delays. Wait until connected and the synchronisation finishes? Bitmesaĝo ne estas konektita al la reto. Se vi eliros nun, tio povas igi malfruiĝojn de liveradoj. Ĉu atendi ĝis ĝi konektos kaj la samtempigado finiĝos? - + Waiting for network connection... Atendado je retkonekto… - + Waiting for finishing synchronisation... Atendado ĝis samtempigado finiĝos… + + + You have already set a notification sound for this address book entry. Do you really want to overwrite it? + Vi jam agordis sciigan sonon por tiu ĉi adreso. Ĉu vi volas anstataŭigi ĝin? + MessageView From 680cc2cafc5c209f08e61aa54a68dd4b62676674 Mon Sep 17 00:00:00 2001 From: PyBitmessage Translations Date: Thu, 21 Sep 2017 19:16:40 +0200 Subject: [PATCH 219/407] Auto-updated language de from transifex --- src/translations/bitmessage_de.qm | Bin 97437 -> 98426 bytes src/translations/bitmessage_de.ts | 364 ++++++++++++++++-------------- 2 files changed, 192 insertions(+), 172 deletions(-) diff --git a/src/translations/bitmessage_de.qm b/src/translations/bitmessage_de.qm index 6bef4d31ab079830dc5d9da8359206caf3d17d48..63ccd5254141b427e57228bcf0ec9e72a1c935d7 100644 GIT binary patch delta 3809 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 dB_-bI~&?}oKv$R83JlMr){{ozafwlku delta 3327 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 KfzIrb-~RyYKIA0; diff --git a/src/translations/bitmessage_de.ts b/src/translations/bitmessage_de.ts index 99505de8..df53838a 100644 --- a/src/translations/bitmessage_de.ts +++ b/src/translations/bitmessage_de.ts @@ -58,12 +58,12 @@ EmailGatewayRegistrationDialog - + Registration failed: Registrierung fehlgeschlagen: - + The requested email address is not available, please try a new one. Fill out the new desired email address (including @mailchuck.com) below: Die gewünschte E-Mailaddresse ist nicht verfügbar, bitte probieren Sie eine neue. Die gewünschte E-Mailaddresse (inkl. @mailchuck.com) unten ausfüllen: @@ -174,52 +174,52 @@ Bitte geben Sie die gewünschte E-Mail-Adresse (inkl. @mailchuck.com) unten ein: MainWindow - + Reply to sender Dem Absender antworten - + Reply to channel Antworten in den Chan - + Add sender to your Address Book Absender zum Adressbuch hinzufügen - + Add sender to your Blacklist Absender in die Blacklist eintragen - + Move to Trash In den Papierkorb verschieben - + Undelete Wiederherstellen - + View HTML code as formatted text HTML als formatierten Text anzeigen - + Save message as... Nachricht speichern unter... - + Mark Unread Als ungelesen markieren - + New Neu @@ -244,12 +244,12 @@ Bitte geben Sie die gewünschte E-Mail-Adresse (inkl. @mailchuck.com) unten ein: Adresse in die Zwischenablage kopieren - + Special address behavior... Spezielles Verhalten der Adresse... - + Email gateway E-Mail Schnittstelle @@ -259,37 +259,37 @@ Bitte geben Sie die gewünschte E-Mail-Adresse (inkl. @mailchuck.com) unten ein: Löschen - + Send message to this address Nachricht an diese Adresse senden - + Subscribe to this address Diese Adresse abonnieren - + Add New Address Neue Adresse hinzufügen - + Copy destination address to clipboard Zieladresse in die Zwischenablage kopieren - + Force send Senden erzwingen - + One of your addresses, %1, is an old version 1 address. Version 1 addresses are no longer supported. May we delete it now? Eine Ihrer Adressen, %1, ist eine alte Adresse der Version 1 und wird nicht mehr unterstützt. Soll sie jetzt gelöscht werden? - + Waiting for their encryption key. Will request it again soon. Warte auf den Verschlüsselungscode. Wird bald erneut angefordert. @@ -299,17 +299,17 @@ Bitte geben Sie die gewünschte E-Mail-Adresse (inkl. @mailchuck.com) unten ein: - + Queued. In Warteschlange. - + Message sent. Waiting for acknowledgement. Sent at %1 Nachricht gesendet. Warte auf Bestätigung. Zeitpunkt der Sendung: %1 - + Message sent. Sent at %1 Nachricht gesendet. Zeitpunkt der Sendung: %1 @@ -319,47 +319,47 @@ Bitte geben Sie die gewünschte E-Mail-Adresse (inkl. @mailchuck.com) unten ein: - + Acknowledgement of the message received %1 Bestätigung der Nachricht erhalten %1 - + Broadcast queued. Rundruf in Warteschlange. - + Broadcast on %1 Rundruf um %1 - + Problem: The work demanded by the recipient is more difficult than you are willing to do. %1 Problem: Die vom Empfänger geforderte Arbeit ist schwerer als Sie bereit sind, zu berechnen. %1 - + Problem: The recipient's encryption key is no good. Could not encrypt message. %1 Problem: Der Verschlüsselungscode des Empfängers ist nicht in Ordnung. Nachricht konnte nicht verschlüsselt werden. %1 - + Forced difficulty override. Send should start soon. Schwierigkeitslimit überschrieben. Senden sollte bald beginnen. - + Unknown status: %1 %2 Unbekannter Status: %1 %2 - + Not Connected Nicht verbunden - + Show Bitmessage Bitmessage anzeigen @@ -369,12 +369,12 @@ Bitte geben Sie die gewünschte E-Mail-Adresse (inkl. @mailchuck.com) unten ein: Senden - + Subscribe Abonnieren - + Channel Chan @@ -384,12 +384,12 @@ Bitte geben Sie die gewünschte E-Mail-Adresse (inkl. @mailchuck.com) unten ein: Beenden - + You may manage your keys by editing the keys.dat file stored in the same directory as this program. It is important that you back up this file. Sie können Ihre Schlüssel verwalten, indem Sie die keys.dat bearbeiten, die im gleichen Ordner wie das Programm liegt. Es ist empfehlenswert, vorher ein Backup dieser Datei anzulegen. - + You may manage your keys by editing the keys.dat file stored in %1 It is important that you back up this file. @@ -398,17 +398,17 @@ It is important that you back up this file. Es ist empfehlenswert, vorher ein Backup dieser Datei anzulegen. - + Open keys.dat? Die keys.dat öffnen? - + You may manage your keys by editing the keys.dat file stored in the same directory as this program. It is important that you back up this file. Would you like to open the file now? (Be sure to close Bitmessage before making any changes.) Sie können Ihre Schlüssel verwalten, indem Sie die keys.dat bearbeiten, die im gleichen Ordner wie das Programm liegt. Es ist empfehlenswert, vorher ein Backup dieser Datei anzulegen. Möchten Sie die Datei jetzt öffnen? (Stellen Sie sicher, dass Sie Bitmessage beendet haben, bevor Sie etwas ändern.) - + You may manage your keys by editing the keys.dat file stored in %1 It is important that you back up this file. Would you like to open the file now? (Be sure to close Bitmessage before making any changes.) @@ -418,37 +418,37 @@ Es ist empfehlenswert, vorher ein Backup dieser Datei anzulegen. Möchten Sie di (Stellen Sie sicher, dass Sie Bitmessage beendet haben, bevor Sie etwas ändern.) - + Delete trash? Papierkorb leeren? - + Are you sure you want to delete all trashed messages? Sind Sie sicher, dass Sie alle Nachrichten im Papierkorb löschen möchten? - + bad passphrase Falsches Passwort - + You must type your passphrase. If you don't have one then this is not the form for you. Sie müssen Ihr Passwort eingeben. Wenn Sie keins haben, ist dies das falsche Formular für Sie. - + Bad address version number Falsche Addressenversionsnummer - + Your address version number must be a number: either 3 or 4. Die Addressenversionsnummer muss eine Zahl sein, entweder 3 oder 4. - + Your address version number must be either 3 or 4. Die Addressenversionnsnummer muss entweder 3 oder 4 sein. @@ -518,22 +518,22 @@ Es ist empfehlenswert, vorher ein Backup dieser Datei anzulegen. Möchten Sie di - + Connection lost Verbindung verloren - + Connected Verbunden - + Message trashed Nachricht in den Papierkorb verschoben - + The TTL, or Time-To-Live is the length of time that the network will hold the message. The recipient must get it during this time. If your Bitmessage client does not hear an acknowledgement, it will resend the message automatically. The longer the Time-To-Live, the @@ -541,17 +541,17 @@ Es ist empfehlenswert, vorher ein Backup dieser Datei anzulegen. Möchten Sie di Die Haltbarkeit, oder Time-To-Live, ist die Dauer, für die das Netzwerk die Nachricht speichern wird. Der Empfänger muss sie während dieser Zeit empfangen. Wenn Ihr Bitmessage-Client keine Empfangsbestätigung erhält, wird die Nachricht automatisch erneut verschickt. Je länger die Time-To-Live, desto mehr Arbeit muss Ihr Rechner verrichten, um die Nachricht zu senden. Eine Time-To-Live von vier oder fünf Tagen ist meist ausreichend. - + Message too long Narchricht zu lang - + The message that you are trying to send is too long by %1 bytes. (The maximum is 261644 bytes). Please cut it down before sending. Die Nachricht, die Sie zu senden versuchen, ist %1 Byte zu lang. (Maximum 261.644 Bytes). Bitte verringern Sie ihre Größe vor dem Senden. - + Error: Your account wasn't registered at an email gateway. Sending registration now as %1, please wait for the registration to be processed before retrying sending. Fehler: Ihr Konto war an keiner E-Mail Schnittstelle registriert. Registrierung als %1 wird versandt, bitte vor einem erneutem Sendeversuch auf die Registrierungsverarbeitung warten. @@ -596,67 +596,67 @@ Es ist empfehlenswert, vorher ein Backup dieser Datei anzulegen. Möchten Sie di - + Error: You must specify a From address. If you don't have one, go to the 'Your Identities' tab. Fehler: Sie müssen eine Absenderadresse auswählen. Sollten Sie keine haben, wechseln Sie zum Reiter "Ihre Identitäten". - + Address version number Adressversion - + Concerning the address %1, Bitmessage cannot understand address version numbers of %2. Perhaps upgrade Bitmessage to the latest version. Aufgrund der Adresse %1 kann Bitmessage Adressen mit der Version %2 nicht verarbeiten. Möglicherweise müssen Sie Bitmessage auf die aktuelle Version aktualisieren. - + Stream number Datenstrom Nummer - + Concerning the address %1, Bitmessage cannot handle stream numbers of %2. Perhaps upgrade Bitmessage to the latest version. Aufgrund der Adresse %1 kann Bitmessage den Datenstrom mit der Version %2 nicht verarbeiten. Möglicherweise müssen Sie Bitmessage auf die aktuelle Version aktualisieren. - + Warning: You are currently not connected. Bitmessage will do the work necessary to send the message but it won't send until you connect. Warnung: Sie sind aktuell nicht verbunden. Bitmessage wird die nötige Arbeit zum versenden verrichten, aber erst senden, wenn Sie verbunden sind. - + Message queued. Nachricht befindet sich in der Warteschleife. - + Your 'To' field is empty. Ihr "Empfänger"-Feld ist leer. - + Right click one or more entries in your address book and select 'Send message to this address'. Klicken Sie mit rechts auf einen oder mehrere Einträge aus Ihrem Adressbuch und wählen Sie "Nachricht an diese Adresse senden". - + Fetched address from namecoin identity. Adresse aus Namecoin Identität geholt. - + New Message Neue Nachricht - + From - Von + - + Sending email gateway registration request Der Registrierungsantrag für die E-Mail Schnittstelle wird versandt. @@ -671,142 +671,142 @@ Es ist empfehlenswert, vorher ein Backup dieser Datei anzulegen. Möchten Sie di Die von Ihnen eingegebene Adresse ist ungültig, sie wird ignoriert. - + Error: You cannot add the same address to your address book twice. Try renaming the existing one if you want. Fehler: Sie können eine Adresse nicht doppelt im Adressbuch speichern. Sie können jedoch die bereits eingetragene umbenennen. - + Error: You cannot add the same address to your subscriptions twice. Perhaps rename the existing one if you want. Fehler: Dieselbe Adresse kann nicht doppelt in die Abonnements eingetragen werden. Sie können jedoch die bereits eingetragene umbenennen. - + Restart Neustart - + You must restart Bitmessage for the port number change to take effect. Sie müssen Bitmessage neu starten, um den geänderten Port zu verwenden. - + Bitmessage will use your proxy from now on but you may want to manually restart Bitmessage now to close existing connections (if any). Bitmessage wird ab sofort den Proxy-Server verwenden, aber eventuell möchten Sie Bitmessage neu starten um bereits bestehende Verbindungen zu schließen. - + Number needed Zahl erforderlich - + Your maximum download and upload rate must be numbers. Ignoring what you typed. Ihre maximale Herungerlade- und Hochladegeschwindigkeit müssen Zahlen sein. Die eingetragenen Werte werden ignoriert. - + Will not resend ever Wird nie wiederversendet - + Note that the time limit you entered is less than the amount of time Bitmessage waits for the first resend attempt therefore your messages will never be resent. Bitte beachten Sie, dass der eingetratene Dauer kürzer ist als die, die Bitmessage auf das erste Wiederversenden wartet. Deswegen werden Ihre Nachrichten nie wiederversendet. - + Sending email gateway unregistration request E-Mail Schnittestellen-Abmeldeantrag wird versandt - + Sending email gateway status request E-Mail Schnittestellen Statusantrag wird versandt - + Passphrase mismatch Kennwort stimmt nicht überein - + The passphrase you entered twice doesn't match. Try again. Die von Ihnen eingegebenen Kennwörter sind nicht identisch. Bitte neu versuchen. - + Choose a passphrase Wählen Sie ein Kennwort - + You really do need a passphrase. Sie benötigen wirklich ein Kennwort. - + Address is gone Adresse ist verloren - + Bitmessage cannot find your address %1. Perhaps you removed it? Bitmessage kann Ihre Adresse %1 nicht finden. Haben Sie sie gelöscht? - + Address disabled Adresse deaktiviert - + Error: The address from which you are trying to send is disabled. You'll have to enable it on the 'Your Identities' tab before using it. Fehler: Die Adresse von der Sie versuchen zu senden ist deaktiviert. Sie müssen sie unter dem Reiter "Ihre Identitäten" aktivieren bevor Sie fortfahren. - + Entry added to the Address Book. Edit the label to your liking. Eintrag dem Adressbuch hinzugefügt. Editieren Sie den Eintrag nach Belieben. - + Entry added to the blacklist. Edit the label to your liking. Eintrag in die Blacklist hinzugefügt. Die Beschriftung können Sie ändern. - + Error: You cannot add the same address to your blacklist twice. Try renaming the existing one if you want. Fehler: Dieselbe Addresse kann nicht doppelt in die Blacklist eingetragen werden. Sie können jedoch die bereits eingetragene umbenennen. - + Moved items to trash. Objekt(e) in den Papierkorb verschoben. - + Undeleted item. Nachricht wiederhergestellt. - + Save As... Speichern unter... - + Write error. Fehler beim Speichern. - + No addresses selected. Keine Adresse ausgewählt. - + If you delete the subscription, messages that you already received will become inaccessible. Maybe you can consider disabling the subscription instead. Disabled subscriptions will not receive new messages, but you can still view messages you already received. Are you sure you want to delete the subscription? @@ -815,7 +815,7 @@ Are you sure you want to delete the subscription? Sind Sie sicher, dass Sie das Abonnement löschen möchten? - + If you delete the channel, messages that you already received will become inaccessible. Maybe you can consider disabling the channel instead. Disabled channels will not receive new messages, but you can still view messages you already received. Are you sure you want to delete the channel? @@ -824,92 +824,92 @@ Are you sure you want to delete the channel? Sind Sie sicher, dass Sie das Chan löschen möchten? - + Do you really want to remove this avatar? Wollen Sie diesen Avatar wirklich entfernen? - + You have already set an avatar for this address. Do you really want to overwrite it? Sie haben bereits einen Avatar für diese Adresse gewählt. Wollen Sie ihn wirklich überschreiben? - + Start-on-login not yet supported on your OS. Mit Betriebssystem starten, noch nicht von Ihrem Betriebssystem unterstützt - + Minimize-to-tray not yet supported on your OS. Ins System Tray minimieren von Ihrem Betriebssytem noch nicht unterstützt. - + Tray notifications not yet supported on your OS. Trach-Benachrichtigungen von Ihrem Betriebssystem noch nicht unterstützt. - + Testing... teste... - + This is a chan address. You cannot use it as a pseudo-mailing list. Dies ist eine Chan-Adresse. Sie können sie nicht als Pseudo-Mailingliste verwenden. - + The address should start with ''BM-'' Die Adresse sollte mit "BM-" beginnen - + The address is not typed or copied correctly (the checksum failed). Die Adresse wurde nicht korrekt getippt oder kopiert (Prüfsumme falsch). - + The version number of this address is higher than this software can support. Please upgrade Bitmessage. Die Versionsnummer dieser Adresse ist höher als diese Software unterstützt. Bitte installieren Sie die neuste Bitmessage Version. - + The address contains invalid characters. Diese Adresse beinhaltet ungültige Zeichen. - + Some data encoded in the address is too short. Die in der Adresse codierten Daten sind zu kurz. - + Some data encoded in the address is too long. Die in der Adresse codierten Daten sind zu lang. - + Some data encoded in the address is malformed. Einige in der Adresse kodierten Daten sind ungültig. - + Enter an address above. Eine Addresse oben ausfüllen. - + Address is an old type. We cannot display its past broadcasts. Alter Addressentyp. Wir können deren vorige Rundrufe nicht anzeigen. - + There are no recent broadcasts from this address to display. Es gibt keine neuen Rundrufe von dieser Adresse die angezeigt werden können. - + You are using TCP port %1. (This can be changed in the settings). Sie benutzen TCP-Port %1 (Dieser kann in den Einstellungen verändert werden). @@ -1119,47 +1119,47 @@ Sind Sie sicher, dass Sie das Chan löschen möchten? Neuen Eintrag erstellen - + Display the %1 recent broadcast(s) from this address. Die letzten %1 Rundruf(e) von dieser Addresse anzeigen. - + New version of PyBitmessage is available: %1. Download it from https://github.com/Bitmessage/PyBitmessage/releases/latest Neue Version von PyBitmessage steht zur Verfügung: %1. Sie können sie von https://github.com/Bitmessage/PyBitmessage/releases/latest herunterladen. - + Waiting for PoW to finish... %1% Warte auf Abschluss von Berechnungen (PoW)... %1% - + Shutting down Pybitmessage... %1% PyBitmessage wird beendet... %1% - + Waiting for objects to be sent... %1% Warte auf Versand von Objekten... %1% - + Saving settings... %1% Einstellungen werden gespeichert... %1% - + Shutting down core... %1% Kern wird beendet... %1% - + Stopping notifications... %1% Beende Benachrichtigungen... %1% - + Shutdown imminent... %1% Unmittelbar vor Beendung... %1% @@ -1169,17 +1169,17 @@ Sind Sie sicher, dass Sie das Chan löschen möchten? %n Stunde%n Stunden - + %n day(s) %n Tag%n Tage - + Shutting down PyBitmessage... %1% PyBitmessage wird beendet... %1% - + Sent Gesendet @@ -1224,85 +1224,85 @@ Sind Sie sicher, dass Sie das Chan löschen möchten? Warnung: Datenträger ist voll. Bitmessage wird jetzt beendet. - + Error! Could not find sender address (your address) in the keys.dat file. Fehler! Konnte die Absenderadresse (Ihre Adresse) in der keys.dat-Datei nicht finden. - + Doing work necessary to send broadcast... Arbeit wird verrichtet, um Rundruf zu verschicken... - + Broadcast sent on %1 Rundruf verschickt um %1 - + Encryption key was requested earlier. Verschlüsselungscode wurde früher angefordert. - + Sending a request for the recipient's encryption key. Anfrage nach dem Verschlüsselungscode des Empfängers wird versendet. - + Looking up the receiver's public key Suche nach dem öffentlichen Schlüssel des Empfängers - + Problem: Destination is a mobile device who requests that the destination be included in the message but this is disallowed in your settings. %1 Problem: Der Empfänger benutzt ein mobiles Gerät und erfordert eine unverschlüsselte Empfängeraddresse. Dies ist in Ihren Einstellungen jedoch nicht zulässig. 1% - + Doing work necessary to send message. There is no required difficulty for version 2 addresses like this. Arbeit für Nachrichtenversand wird verrichtet. Version-2-Addressen wie die des Empfängers haben keine Schweirigkeitserforderungen. - + Doing work necessary to send message. Receiver's required difficulty: %1 and %2 Arbeit für Nachrichtenversand wird errichtet. Vom Empfänger geforderte Schwierigkeit: %1 und %2 - + Problem: The work demanded by the recipient (%1 and %2) is more difficult than you are willing to do. %3 Problem: Die vom Empfänger verlangte Arbeit (%1 und %2) ist schwieriger, als Sie in den Einstellungen erlaubt haben. %3 - + Problem: You are trying to send a message to yourself or a chan but your encryption key could not be found in the keys.dat file. Could not encrypt message. %1 Problem: Sie versuchen, eine Nachricht an sich zu versenden, aber Ihr Schlüssel befindet sich nicht in der keys.dat-Datei. Die Nachricht kann nicht verschlüsselt werden. 1% - + Doing work necessary to send message. Arbeit wird verrichtet, um die Nachricht zu verschicken. - + Message sent. Waiting for acknowledgement. Sent on %1 Nachricht gesendet. Auf Bestätigung wird gewartet. Zeitpunkt der Sendung: %1 - + Doing work necessary to request encryption key. Arbeit wird verrichtet, um den Schlüssel nachzufragen... - + Broadcasting the public key request. This program will auto-retry if they are offline. Anfrage nach dem öffentlichen Schlüssel läuft. Wenn der Besitzer nicht mit dem Netzwerk verbunden ist, wird ein Wiederholungsversuch unternommen. - + Sending public key request. Waiting for reply. Requested at %1 Nachfrage nach dem öffentlichen Schlüssel läuft, auf Antwort wird gewartet. Nachgefragt am %1 @@ -1317,37 +1317,37 @@ Receiver's required difficulty: %1 and %2 UPnP Port-Mapping entfernt - + Mark all messages as read Alle Nachrichten als gelesen markieren - + Are you sure you would like to mark all messages read? Sind Sie sicher, dass Sie alle Nachrichten als gelesen markieren möchten? - + Doing work necessary to send broadcast. Führe Arbeit aus, die notwendig ist zum Senden des Rundspruches. - + Proof of work pending Arbeitsbeweis wird berechnet - + %n object(s) pending proof of work %n Objekt wartet auf Berechnungen%n Objekte warten auf Berechnungen - + %n object(s) waiting to be distributed %n Objekt wartet darauf, verteilt zu werden%n Objekte warten darauf, verteilt zu werden - + Wait until these tasks finish? Warten bis diese Aufgaben erledigt sind? @@ -1407,12 +1407,17 @@ Receiver's required difficulty: %1 and %2 Kann NMControl nicht verstehen. - + Your GPU(s) did not calculate correctly, disabling OpenCL. Please report to the developers. Ihre Grafikkarte hat inkorrekt berechnet, OpenCL wird deaktiviert. Bitte benachrichtigen Sie die Entwickler. - + + Set notification sound... + Benachrichtigungsklang einstellen ... + + + Welcome to easy and secure Bitmessage * send messages to other people @@ -1426,85 +1431,100 @@ Willkommen zu einfachem und sicherem Bitmessage * diskutieren Sie mit anderen Leuten in Chans - + not recommended for chans für Chans nicht empfohlen - + Problems connecting? Try enabling UPnP in the Network Settings Verbindungsprobleme? Versuchen Sie UPnP in den Netzwerkeinstellungen einzuschalten - + + You are trying to send an email instead of a bitmessage. This requires registering with a gateway. Attempt to register? + Sie versuchen, eine E-Mail anstelle einer Bitmessage zu senden. Dies erfordert eine Registrierung bei einer Schnittstelle. Registrierung versuchen? + + + Error: Bitmessage addresses start with BM- Please check the recipient address %1 Fehler: Bitmessage Adressen starten mit BM- Bitte überprüfen Sie die Empfängeradresse %1 - + Error: The recipient address %1 is not typed or copied correctly. Please check it. Fehler: Die Empfängeradresse %1 wurde nicht korrekt getippt oder kopiert. Bitte überprüfen. - + Error: The recipient address %1 contains invalid characters. Please check it. Fehler: Die Empfängeradresse %1 beinhaltet ungültig Zeichen. Bitte überprüfen. - + Error: The version of the recipient address %1 is too high. Either you need to upgrade your Bitmessage software or your acquaintance is being clever. Fehler: Die Empfängerdresseversion von %1 ist zu hoch. Entweder Sie müssen Ihre Bitmessage Software aktualisieren oder Ihr Bekannter ist sehr clever. - + Error: Some data encoded in the recipient address %1 is too short. There might be something wrong with the software of your acquaintance. Fehler: Einige Daten die in der Empfängerdresse %1 codiert sind, sind zu kurz. Es könnte sein, dass etwas mit der Software Ihres Bekannten nicht stimmt. - + Error: Some data encoded in the recipient address %1 is too long. There might be something wrong with the software of your acquaintance. Fehler: Einige Daten die in der Empfängeradresse %1 codiert sind, sind zu lang. Es könnte sein, dass etwas mit der Software Ihres Bekannten nicht stimmt. - + Error: Some data encoded in the recipient address %1 is malformed. There might be something wrong with the software of your acquaintance. Fehler: Einige codierte Daten in der Empfängeradresse %1 sind ungültig. Es könnte etwas mit der Software Ihres Bekannten sein. - + Error: Something is wrong with the recipient address %1. Fehler: Mit der Empfängeradresse %1 stimmt etwas nicht. - + + From %1 + Von %1 + + + Synchronisation pending Synchronisierung läuft - + Bitmessage hasn't synchronised with the network, %n object(s) to be downloaded. If you quit now, it may cause delivery delays. Wait until the synchronisation finishes? Bitmessage ist nicht synchronisiert, %n Objekt wurde noch nicht heruntergeladen. Wenn Sie das Programm jetzt beenden, kann es zu Zustellverzögerungen kommen. Warten bis die Synchronisierung abgeschlossen ist?Bitmessage ist nicht synchronisiert, %n Objekte wurden noch nicht heruntergeladen. Wenn Sie das Programm jetzt beenden, kann es zu Zustellverzögerungen kommen. Warten bis die Synchronisierung abgeschlossen ist? - + Not connected Nicht verbunden - + Bitmessage isn't connected to the network. If you quit now, it may cause delivery delays. Wait until connected and the synchronisation finishes? Bitmessage ist nicht mit dem Netzwerk verbunden. Wenn Sie das Programm jetzt beenden, kann es zu Zustellverzögerungen kommen. Warten bis eine Verbindung besteht und die Synchronisierung abgeschlossen ist? - + Waiting for network connection... Warte auf Netzwerkverbindung... - + Waiting for finishing synchronisation... Warte auf Synchronisationsabschluss... + + + You have already set a notification sound for this address book entry. Do you really want to overwrite it? + Sie haben bereits einen Benachrichtigungsklang für diesen Adressbucheintrag gesetzt. Möchten Sie ihn wirklich überschreiben? + MessageView @@ -1995,7 +2015,7 @@ Die Zufallszahlen-Option ist standardmässig gewählt, jedoch haben deterministi List of streams negotiated between you and the peer - Liste der zwischen Ihnen und dem Peer ausgehandelten Datenströme + Liste der zwischen Ihnen und dem Peer ausgehandelter Datenströme @@ -2092,17 +2112,17 @@ Die Zufallszahlen-Option ist standardmässig gewählt, jedoch haben deterministi proofofwork - + C PoW module built successfully. C-PoW-Modul erfolgreich erstellt. - + Failed to build C PoW module. Please build it manually. Erstellung vom C-PoW-Modul fehlgeschlagen. Bitte erstellen Sie es manuell. - + C PoW module unavailable. Please build it. C-PoW-Modul nicht verfügbar. Bitte erstellen Sie es. From abea17ded94bdda1d61071c8cba8d878f78c17e9 Mon Sep 17 00:00:00 2001 From: Peter Surda Date: Thu, 21 Sep 2017 19:57:34 +0200 Subject: [PATCH 220/407] setup.py check if directory already exists --- setup.py | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/setup.py b/setup.py index 44356e7e..0851b78f 100644 --- a/setup.py +++ b/setup.py @@ -11,10 +11,16 @@ from src.version import softwareVersion class InstallCmd(install): def run(self): # prepare icons directories - os.makedirs('desktop/icons/scalable') + try: + os.makedirs('desktop/icons/scalable') + except os.error: + pass shutil.copyfile( 'desktop/can-icon.svg', 'desktop/icons/scalable/pybitmessage.svg') - os.makedirs('desktop/icons/24x24') + try: + os.makedirs('desktop/icons/24x24') + except os.error: + pass shutil.copyfile( 'desktop/icon24.png', 'desktop/icons/24x24/pybitmessage.png') From a8ab574c3e9b4d57ff94f78d9124d504de274d10 Mon Sep 17 00:00:00 2001 From: PyBitmessage Translations Date: Thu, 21 Sep 2017 21:06:53 +0200 Subject: [PATCH 221/407] Auto-updated language pl from transifex --- src/translations/bitmessage_pl.qm | Bin 90995 -> 91798 bytes src/translations/bitmessage_pl.ts | 366 ++++++++++++++++-------------- 2 files changed, 193 insertions(+), 173 deletions(-) diff --git a/src/translations/bitmessage_pl.qm b/src/translations/bitmessage_pl.qm index 4ecb0a11d0dffee1d30516e399739dab8ff4aab8..1b51288030ccc843e1f67e161a1c72f4791723d1 100644 GIT binary patch delta 3878 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 gbfiVbt&^rD^J1>smgMJKS!K9hdQ>v EmailGatewayRegistrationDialog - + Registration failed: Rejestracja nie powiodła się: - + The requested email address is not available, please try a new one. Fill out the new desired email address (including @mailchuck.com) below: Wybrany adres e-mail nie jest dostępny, proszę spróbować inny. Wpisz adres poniżej (razem z końcówką @mailchuck.com): @@ -170,52 +170,52 @@ Wprowadź wybrany adres e-mail (razem z końcówką @mailchuck.com) poniżej: MainWindow - + Reply to sender Odpowiedz do nadawcy - + Reply to channel Odpowiedz do kanału - + Add sender to your Address Book Dodaj nadawcę do Książki Adresowej - + Add sender to your Blacklist Dodaj nadawcę do Listy Blokowanych - + Move to Trash Przenieś do kosza - + Undelete Przywróć - + View HTML code as formatted text Wyświetl kod HTML w postaci sformatowanej - + Save message as... Zapisz wiadomość jako… - + Mark Unread Oznacz jako nieprzeczytane - + New Nowe @@ -240,12 +240,12 @@ Wprowadź wybrany adres e-mail (razem z końcówką @mailchuck.com) poniżej:Kopiuj adres do schowka - + Special address behavior... Specjalne zachowanie adresu… - + Email gateway Przekaźnik e-mail @@ -255,37 +255,37 @@ Wprowadź wybrany adres e-mail (razem z końcówką @mailchuck.com) poniżej:Usuń - + Send message to this address Wyślij wiadomość pod ten adres - + Subscribe to this address Subskrybuj ten adres - + Add New Address Dodaj nowy adres - + Copy destination address to clipboard Kopiuj adres odbiorcy do schowka - + Force send Wymuś wysłanie - + One of your addresses, %1, is an old version 1 address. Version 1 addresses are no longer supported. May we delete it now? Jeden z adresów, %1, jest starym adresem wersji 1. Adresy tej wersji nie są już wspierane. Usunąć go? - + Waiting for their encryption key. Will request it again soon. Oczekiwanie na klucz szyfrujący odbiorcy. Niedługo nastąpi ponowne wysłanie o niego prośby. @@ -295,17 +295,17 @@ Wprowadź wybrany adres e-mail (razem z końcówką @mailchuck.com) poniżej: - + Queued. W kolejce do wysłania. - + Message sent. Waiting for acknowledgement. Sent at %1 Wiadomość wysłana. Oczekiwanie na potwierdzenie odbioru. Wysłano o %1 - + Message sent. Sent at %1 Wiadomość wysłana. Wysłano o %1 @@ -315,47 +315,47 @@ Wprowadź wybrany adres e-mail (razem z końcówką @mailchuck.com) poniżej: - + Acknowledgement of the message received %1 Otrzymano potwierdzenie odbioru wiadomości %1 - + Broadcast queued. Przekaz w kolejce do wysłania. - + Broadcast on %1 Wysłana o %1 - + Problem: The work demanded by the recipient is more difficult than you are willing to do. %1 Problem: dowód pracy wymagany przez odbiorcę jest trudniejszy niż zaakceptowany przez Ciebie. %1 - + Problem: The recipient's encryption key is no good. Could not encrypt message. %1 Problem: klucz szyfrujący odbiorcy jest nieprawidłowy. Nie można zaszyfrować wiadomości. %1 - + Forced difficulty override. Send should start soon. Wymuszono ominięcie trudności. Wysłanie zostanie wkrótce rozpoczęte. - + Unknown status: %1 %2 Nieznany status: %1 %2 - + Not Connected Brak połączenia - + Show Bitmessage Pokaż Bitmessage @@ -365,12 +365,12 @@ Wprowadź wybrany adres e-mail (razem z końcówką @mailchuck.com) poniżej:Wyślij - + Subscribe Subskrybuj - + Channel Kanał @@ -380,12 +380,12 @@ Wprowadź wybrany adres e-mail (razem z końcówką @mailchuck.com) poniżej:Zamknij - + You may manage your keys by editing the keys.dat file stored in the same directory as this program. It is important that you back up this file. Możesz zarządzać swoimi kluczami edytując plik keys.dat znajdujący się w tym samym katalogu co program. Zaleca się zrobienie kopii zapasowej tego pliku. - + You may manage your keys by editing the keys.dat file stored in %1 It is important that you back up this file. @@ -394,17 +394,17 @@ It is important that you back up this file. Zaleca się zrobienie kopii zapasowej tego pliku. - + Open keys.dat? Otworzyć plik keys.dat? - + You may manage your keys by editing the keys.dat file stored in the same directory as this program. It is important that you back up this file. Would you like to open the file now? (Be sure to close Bitmessage before making any changes.) Możesz zarządzać swoimi kluczami edytując plik keys.dat znajdujący się w tym samym katalogu co program. Zaleca się zrobienie kopii zapasowej tego pliku. Czy chcesz otworzyć ten plik teraz? (Zamknij Bitmessage, przed wprowadzeniem jakichkolwiek zmian.) - + You may manage your keys by editing the keys.dat file stored in %1 It is important that you back up this file. Would you like to open the file now? (Be sure to close Bitmessage before making any changes.) @@ -413,37 +413,37 @@ It is important that you back up this file. Would you like to open the file now? Zaleca się zrobienie kopii zapasowej tego pliku. Czy chcesz otworzyć ten plik teraz? (Zamknij Bitmessage przed wprowadzeniem jakichkolwiek zmian.) - + Delete trash? Opróżnić kosz? - + Are you sure you want to delete all trashed messages? Czy na pewno usunąć wszystkie wiadomości z kosza? - + bad passphrase nieprawidłowe hasło - + You must type your passphrase. If you don't have one then this is not the form for you. Musisz wpisać swoje hasło. Jeżeli go nie posiadasz, to ten formularz nie jest dla Ciebie. - + Bad address version number Nieprawidłowy numer wersji adresu - + Your address version number must be a number: either 3 or 4. Twój numer wersji adresu powinien wynosić: 3 lub 4. - + Your address version number must be either 3 or 4. Twój numer wersji adresu powinien wynosić: 3 lub 4. @@ -513,22 +513,22 @@ Zaleca się zrobienie kopii zapasowej tego pliku. Czy chcesz otworzyć ten plik - + Connection lost Połączenie utracone - + Connected Połączono - + Message trashed Wiadomość usunięta - + The TTL, or Time-To-Live is the length of time that the network will hold the message. The recipient must get it during this time. If your Bitmessage client does not hear an acknowledgement, it will resend the message automatically. The longer the Time-To-Live, the @@ -539,17 +539,17 @@ Im dłuższy TTL, tym więcej pracy będzie musiał wykonac komputer wysyłając Zwykle 4-5 dniowy TTL jest odpowiedni. - + Message too long Wiadomość zbyt długa - + The message that you are trying to send is too long by %1 bytes. (The maximum is 261644 bytes). Please cut it down before sending. Wiadomość jest za długa o %1 bajtów (maksymalna długość wynosi 261644 bajty). Przed wysłaniem należy ją skrócić. - + Error: Your account wasn't registered at an email gateway. Sending registration now as %1, please wait for the registration to be processed before retrying sending. Błąd: Twoje konto nie było zarejestrowane w bramce poczty. Rejestrowanie jako %1, proszę poczekać na zakończenie procesu przed ponowną próbą wysłania wiadomości. @@ -594,67 +594,67 @@ Zwykle 4-5 dniowy TTL jest odpowiedni. - + Error: You must specify a From address. If you don't have one, go to the 'Your Identities' tab. Błąd: musisz wybrać adres wysyłania. Jeżeli go nie posiadasz, przejdź do zakładki 'Twoje tożsamości'. - + Address version number Numer wersji adresu - + Concerning the address %1, Bitmessage cannot understand address version numbers of %2. Perhaps upgrade Bitmessage to the latest version. Odnośnie adresu %1, Bitmessage nie potrafi odczytać wersji adresu %2. Może uaktualnij Bitmessage do najnowszej wersji. - + Stream number Numer strumienia - + Concerning the address %1, Bitmessage cannot handle stream numbers of %2. Perhaps upgrade Bitmessage to the latest version. Odnośnie adresu %1, Bitmessage nie potrafi operować na strumieniu adresu %2. Może uaktualnij Bitmessage do najnowszej wersji. - + Warning: You are currently not connected. Bitmessage will do the work necessary to send the message but it won't send until you connect. Uwaga: nie jesteś obecnie połączony. Bitmessage wykona niezbędną pracę do wysłania wiadomości, ale nie wyśle jej póki się nie połączysz. - + Message queued. W kolejce do wysłania - + Your 'To' field is empty. Pole 'Do' jest puste - + Right click one or more entries in your address book and select 'Send message to this address'. Użyj prawego przycisku myszy na adresie z książki adresowej i wybierz opcję "Wyślij wiadomość do tego adresu". - + Fetched address from namecoin identity. Pobrano adres z identyfikatora Namecoin. - + New Message Nowa wiadomość - + From - Od + - + Sending email gateway registration request Wysyłanie zapytania o rejestrację na bramce poczty @@ -669,142 +669,142 @@ Zwykle 4-5 dniowy TTL jest odpowiedni. Wprowadzono niewłaściwy adres, który został zignorowany. - + Error: You cannot add the same address to your address book twice. Try renaming the existing one if you want. Błąd: Adres znajduje się już w książce adresowej. - + Error: You cannot add the same address to your subscriptions twice. Perhaps rename the existing one if you want. Błąd: Adres znajduje się już na liście subskrybcji. - + Restart Uruchom ponownie - + You must restart Bitmessage for the port number change to take effect. Musisz zrestartować Bitmessage, aby zmiana numeru portu weszła w życie. - + Bitmessage will use your proxy from now on but you may want to manually restart Bitmessage now to close existing connections (if any). Bitmessage będzie of teraz korzystał z serwera proxy, ale możesz ręcznie zrestartować Bitmessage, aby zamknąć obecne połączenia (jeżeli występują). - + Number needed Wymagany numer - + Your maximum download and upload rate must be numbers. Ignoring what you typed. Maksymalne prędkości wysyłania i pobierania powinny być liczbami. Zignorowano zmiany. - + Will not resend ever Nigdy nie wysyłaj ponownie - + Note that the time limit you entered is less than the amount of time Bitmessage waits for the first resend attempt therefore your messages will never be resent. Zauważ, że wpisany limit czasu wynosi mniej niż czas, który Bitmessage czeka przed pierwszą ponowną próbą wysłania wiadomości, więc Twoje wiadomości nie zostaną nigdy wysłane ponownie. - + Sending email gateway unregistration request Wysyłanie zapytania o wyrejestrowanie z bramki poczty - + Sending email gateway status request Wysyłanie zapytania o stan bramki poczty - + Passphrase mismatch Hasła różnią się - + The passphrase you entered twice doesn't match. Try again. Hasła, które wpisałeś nie pasują. Spróbuj ponownie. - + Choose a passphrase Wpisz hasło - + You really do need a passphrase. Naprawdę musisz wpisać hasło. - + Address is gone Adres zniknął - + Bitmessage cannot find your address %1. Perhaps you removed it? Bitmessage nie może odnaleźć Twojego adresu %1. Może go usunąłeś? - + Address disabled Adres nieaktywny - + Error: The address from which you are trying to send is disabled. You'll have to enable it on the 'Your Identities' tab before using it. Błąd: adres, z którego próbowałeś wysłać wiadomość jest nieaktywny. Włącz go w zakładce 'Twoje tożsamości' zanim go użyjesz. - + Entry added to the Address Book. Edit the label to your liking. Dodano wpis do książki adresowej. Można teraz zmienić jego nazwę. - + Entry added to the blacklist. Edit the label to your liking. Dodano wpis do listy blokowanych. Można teraz zmienić jego nazwę. - + Error: You cannot add the same address to your blacklist twice. Try renaming the existing one if you want. Błąd: Adres znajduje się już na liście blokowanych. - + Moved items to trash. Przeniesiono wiadomości do kosza. - + Undeleted item. Przywróć wiadomość. - + Save As... Zapisz jako… - + Write error. Błąd zapisu. - + No addresses selected. Nie wybrano adresu. - + If you delete the subscription, messages that you already received will become inaccessible. Maybe you can consider disabling the subscription instead. Disabled subscriptions will not receive new messages, but you can still view messages you already received. Are you sure you want to delete the subscription? @@ -813,7 +813,7 @@ Are you sure you want to delete the subscription? Czy na pewno chcesz usunąć tę subskrypcję? - + If you delete the channel, messages that you already received will become inaccessible. Maybe you can consider disabling the channel instead. Disabled channels will not receive new messages, but you can still view messages you already received. Are you sure you want to delete the channel? @@ -822,92 +822,92 @@ Are you sure you want to delete the channel? Czy na pewno chcesz usunąć ten kanał? - + Do you really want to remove this avatar? Czy na pewno chcesz usunąć ten awatar? - + You have already set an avatar for this address. Do you really want to overwrite it? Już ustawiłeś awatar dla tego adresu. Czy na pewno chcesz go nadpisać? - + Start-on-login not yet supported on your OS. Start po zalogowaniu jeszcze nie jest wspierany pod Twoim systemem. - + Minimize-to-tray not yet supported on your OS. Minimalizacja do zasobnika nie jest jeszcze wspierana pod Twoim systemem. - + Tray notifications not yet supported on your OS. Powiadomienia w zasobniku nie są jeszcze wspierane pod Twoim systemem. - + Testing... Testowanie… - + This is a chan address. You cannot use it as a pseudo-mailing list. To jest adres kanału. Nie możesz go użyć jako pseudo-listy-dyskusyjnej. - + The address should start with ''BM-'' Adres powinien zaczynać sie od "BM-" - + The address is not typed or copied correctly (the checksum failed). Adres nie został skopiowany lub przepisany poprawnie (błąd sumy kontrolnej). - + The version number of this address is higher than this software can support. Please upgrade Bitmessage. Numer wersji tego adresu jest wyższy niż ten program może obsłużyć. Proszę zaktualizować Bitmessage. - + The address contains invalid characters. Adres zawiera nieprawidłowe znaki. - + Some data encoded in the address is too short. Niektóre dane zakodowane w adresie są za krótkie. - + Some data encoded in the address is too long. Niektóre dane zakodowane w adresie są za długie. - + Some data encoded in the address is malformed. Niektóre dane zakodowane w adresie są uszkodzone. - + Enter an address above. Wprowadź adres powyżej. - + Address is an old type. We cannot display its past broadcasts. Adres starego typu - + There are no recent broadcasts from this address to display. Brak niedawnych wiadomości przekazów do wyświetlenia. - + You are using TCP port %1. (This can be changed in the settings). Btimessage używa portu TCP %1. (Można go zmienić w ustawieniach). @@ -1117,47 +1117,47 @@ Czy na pewno chcesz usunąć ten kanał? Dodaj nowy wpis - + Display the %1 recent broadcast(s) from this address. Pokaż %1 ostatnich wiadomości przekazów z tego adresu. - + New version of PyBitmessage is available: %1. Download it from https://github.com/Bitmessage/PyBitmessage/releases/latest Nowa wersja Bitmessage jest dostępna: %1. Pobierz ją z https://github.com/Bitmessage/PyBitmessage/releases/latest - + Waiting for PoW to finish... %1% Oczekiwanie na wykonanie dowodu pracy… %1% - + Shutting down Pybitmessage... %1% Zamykanie PyBitmessage… %1% - + Waiting for objects to be sent... %1% Oczekiwanie na wysłanie obiektów… %1% - + Saving settings... %1% Zapisywanie ustawień… %1% - + Shutting down core... %1% Zamykanie rdzenia programu… %1% - + Stopping notifications... %1% Zatrzymywanie powiadomień… %1% - + Shutdown imminent... %1% Zaraz zamknę… %1% @@ -1167,17 +1167,17 @@ Czy na pewno chcesz usunąć ten kanał? %n godzina%n godziny%n godzin%n godzin - + %n day(s) %n dzień%n dni%n dni%n dni - + Shutting down PyBitmessage... %1% Zamykanie PyBitmessage… %1% - + Sent Wysłane @@ -1222,131 +1222,131 @@ Czy na pewno chcesz usunąć ten kanał? Uwaga: Twój dysk lub partycja jest pełny. Bitmessage zamknie się. - + Error! Could not find sender address (your address) in the keys.dat file. Błąd! Nie można odnaleźć adresu nadawcy (Twojego adresu) w pliku keys.dat. - + Doing work necessary to send broadcast... Wykonywanie dowodu pracy niezbędnego do wysłania przekazu… - + Broadcast sent on %1 Przekaz wysłane o %1 - + Encryption key was requested earlier. Prośba o klucz szyfrujący została już wysłana. - + Sending a request for the recipient's encryption key. Wysyłanie zapytania o klucz szyfrujący odbiorcy. - + Looking up the receiver's public key Wyszukiwanie klucza publicznego odbiorcy - + Problem: Destination is a mobile device who requests that the destination be included in the message but this is disallowed in your settings. %1 Problem: adres docelowy jest urządzeniem przenośnym, które wymaga, aby adres docelowy był zawarty w wiadomości, ale jest to zabronione w Twoich ustawieniach. %1 - + Doing work necessary to send message. There is no required difficulty for version 2 addresses like this. Wykonywanie dowodu pracy niezbędnego do wysłania wiadomości. Nie ma wymaganej trudności dla adresów w wersji 2, takich jak ten adres. - + Doing work necessary to send message. Receiver's required difficulty: %1 and %2 Wykonywanie dowodu pracy niezbędnego do wysłania wiadomości. Odbiorca wymaga trudności: %1 i %2 - + Problem: The work demanded by the recipient (%1 and %2) is more difficult than you are willing to do. %3 Problem: dowód pracy wymagany przez odbiorcę (%1 i %2) jest trudniejszy niż chciałbyś wykonać. %3 - + Problem: You are trying to send a message to yourself or a chan but your encryption key could not be found in the keys.dat file. Could not encrypt message. %1 Problem: próbujesz wysłać wiadomość do siebie lub na kanał, ale Twój klucz szyfrujący nie został znaleziony w pliku keys.dat. Nie można zaszyfrować wiadomości. %1 - + Doing work necessary to send message. Wykonywanie pracy potrzebnej do wysłania wiadomości. - + Message sent. Waiting for acknowledgement. Sent on %1 Wiadomość wysłana. Oczekiwanie na potwierdzenie odbioru. Wysłano o %1 - + Doing work necessary to request encryption key. Wykonywanie pracy niezbędnej do prośby o klucz szyfrujący. - + Broadcasting the public key request. This program will auto-retry if they are offline. Rozsyłanie prośby o klucz publiczny. Program spróbuje ponownie, jeżeli jest on niepołączony. - + Sending public key request. Waiting for reply. Requested at %1 Wysyłanie prośby o klucz publiczny. Oczekiwanie na odpowiedź. Zapytano o %1 - + UPnP port mapping established on port %1 Mapowanie portów UPnP wykonano na porcie %1 - + UPnP port mapping removed Usunięto mapowanie portów UPnP - + Mark all messages as read Oznacz wszystkie jako przeczytane - + Are you sure you would like to mark all messages read? Czy na pewno chcesz oznaczyć wszystkie wiadomości jako przeczytane? - + Doing work necessary to send broadcast. Wykonywanie dowodu pracy niezbędnego do wysłania przekazu. - + Proof of work pending Dowód pracy zawieszony - + %n object(s) pending proof of work Zawieszony dowód pracy %n obiektuZawieszony dowód pracy %n obiektówZawieszony dowód pracy %n obiektówZawieszony dowód pracy %n obiektów - + %n object(s) waiting to be distributed %n obiekt oczekuje na wysłanie%n obiektów oczekuje na wysłanie%n obiektów oczekuje na wysłanie%n obiektów oczekuje na wysłanie - + Wait until these tasks finish? Czy poczekać aż te zadania zostaną zakończone? @@ -1406,12 +1406,17 @@ Odbiorca wymaga trudności: %1 i %2 Nie można zrozumieć NMControl. - + Your GPU(s) did not calculate correctly, disabling OpenCL. Please report to the developers. Twoje procesory graficzne nie obliczyły poprawnie, wyłączam OpenCL. Prosimy zaraportować przypadek twórcom programu. - + + Set notification sound... + Ustaw dźwięk powiadomień… + + + Welcome to easy and secure Bitmessage * send messages to other people @@ -1425,85 +1430,100 @@ Witamy w przyjaznym i bezpiecznym Bitmessage * dyskutuj na kanałach (chany) z innymi ludźmi - + not recommended for chans niezalecany dla kanałów - + Problems connecting? Try enabling UPnP in the Network Settings Problem z połączeniem? Spróbuj włączyć UPnP w ustawieniach sieci. - + + You are trying to send an email instead of a bitmessage. This requires registering with a gateway. Attempt to register? + Próbujesz wysłać e-mail zamiast wiadomość bitmessage. To wymaga zarejestrowania się na bramce. Czy zarejestrować? + + + Error: Bitmessage addresses start with BM- Please check the recipient address %1 Błąd: adresy Bitmessage zaczynają się od BM-. Proszę sprawdzić adres odbiorcy %1. - + Error: The recipient address %1 is not typed or copied correctly. Please check it. Błąd: adres odbiorcy %1 nie został skopiowany lub przepisany poprawnie. Proszę go sprawdzić. - + Error: The recipient address %1 contains invalid characters. Please check it. Błąd: adres odbiorcy %1 zawiera nieprawidłowe znaki. Proszę go sprawdzić. - + Error: The version of the recipient address %1 is too high. Either you need to upgrade your Bitmessage software or your acquaintance is being clever. Błąd: wersja adresu odbiorcy %1 jest za wysoka. Musisz albo zaktualizować Twoje oprogramowanie Bitmessage, albo twój znajomy Cię trolluje. - + Error: Some data encoded in the recipient address %1 is too short. There might be something wrong with the software of your acquaintance. Błąd: niektóre dane zakodowane w adresie odbiorcy %1 są zbyt krótkie. Być może coś nie działa należycie w programie Twojego znajomego. - + Error: Some data encoded in the recipient address %1 is too long. There might be something wrong with the software of your acquaintance. Błąd: niektóre dane zakodowane w adresie odbiorcy %1 są zbyt długie. Być może coś nie działa należycie w programie Twojego znajomego. - + Error: Some data encoded in the recipient address %1 is malformed. There might be something wrong with the software of your acquaintance. Błąd: niektóre dane zakodowane w adresie odbiorcy %1 są uszkodzone. Być może coś nie działa należycie w programie Twojego znajomego. - + Error: Something is wrong with the recipient address %1. Błąd: coś jest nie tak z adresem odbiorcy %1. - + + From %1 + Od %1 + + + Synchronisation pending Synchronizacja zawieszona - + Bitmessage hasn't synchronised with the network, %n object(s) to be downloaded. If you quit now, it may cause delivery delays. Wait until the synchronisation finishes? Bitmessage nie zsynchronizował się z siecią, %n obiekt oczekuje na pobranie. Jeżeli zamkniesz go teraz, może to spowodować opóźnienia dostarczeń. Czy poczekać na zakończenie synchronizacji?Bitmessage nie zsynchronizował się z siecią, %n obiekty oczekują na pobranie. Jeżeli zamkniesz go teraz, może to spowodować opóźnienia dostarczeń. Czy poczekać na zakończenie synchronizacji?Bitmessage nie zsynchronizował się z siecią, %n obiektów oczekuje na pobranie. Jeżeli zamkniesz go teraz, może to spowodować opóźnienia dostarczeń. Czy poczekać na zakończenie synchronizacji?Bitmessage nie zsynchronizował się z siecią, %n obiektów oczekuje na pobranie. Jeżeli zamkniesz go teraz, może to spowodować opóźnienia dostarczeń. Czy poczekać na zakończenie synchronizacji? - + Not connected Niepołączony - + Bitmessage isn't connected to the network. If you quit now, it may cause delivery delays. Wait until connected and the synchronisation finishes? Bitmessage nie połączył się z siecią. Jeżeli zamkniesz go teraz, może to spowodować opóźnienia dostarczeń. Czy poczekać na połączenie i zakończenie synchronizacji? - + Waiting for network connection... Oczekiwanie na połączenie sieciowe… - + Waiting for finishing synchronisation... Oczekiwanie na zakończenie synchronizacji… + + + You have already set a notification sound for this address book entry. Do you really want to overwrite it? + Już ustawiłeś dźwięk powiadomienia dla tego kontaktu. Czy chcesz go zastąpić? + MessageView @@ -2091,17 +2111,17 @@ Generowanie adresów 'losowych' jest wybrane domyślnie, jednak determ proofofwork - + C PoW module built successfully. Moduł C PoW zbudowany poprawnie. - + Failed to build C PoW module. Please build it manually. Nie można zbudować modułu C PoW. Prosimy zbudować go ręcznie. - + C PoW module unavailable. Please build it. Moduł C PoW niedostępny. Prosimy zbudować go. From cfa84cf81a87d85b8b05565b12c195cfd2470fab Mon Sep 17 00:00:00 2001 From: f97ada87 Date: Sat, 23 Sep 2017 07:59:14 +1000 Subject: [PATCH 222/407] change default log output from stdout to stderr --- src/debug.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/debug.py b/src/debug.py index 663bbeeb..83b11835 100644 --- a/src/debug.py +++ b/src/debug.py @@ -64,7 +64,7 @@ def configureLogging(): 'class': 'logging.StreamHandler', 'formatter': 'default', 'level': log_level, - 'stream': 'ext://sys.stdout' + 'stream': 'ext://sys.stderr' }, 'file': { 'class': 'logging.handlers.RotatingFileHandler', From aaa5e9d309f2654b2f84d090f768faeaa048c172 Mon Sep 17 00:00:00 2001 From: Peter Surda Date: Sat, 23 Sep 2017 18:25:41 +0200 Subject: [PATCH 223/407] Windows daemon mode workaround --- src/bitmessagemain.py | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/src/bitmessagemain.py b/src/bitmessagemain.py index 9182b4cb..b015d5bf 100755 --- a/src/bitmessagemain.py +++ b/src/bitmessagemain.py @@ -349,12 +349,13 @@ class Main: shared.thisapp.lockPid = None # indicate we're the final child sys.stdout.flush() sys.stderr.flush() - si = file(os.devnull, 'r') - so = file(os.devnull, 'a+') - se = file(os.devnull, 'a+', 0) - os.dup2(si.fileno(), sys.stdin.fileno()) - os.dup2(so.fileno(), sys.stdout.fileno()) - os.dup2(se.fileno(), sys.stderr.fileno()) + if not sys.platform.startswith('win'): + si = file(os.devnull, 'r') + so = file(os.devnull, 'a+') + se = file(os.devnull, 'a+', 0) + os.dup2(si.fileno(), sys.stdin.fileno()) + os.dup2(so.fileno(), sys.stdout.fileno()) + os.dup2(se.fileno(), sys.stderr.fileno()) def setSignalHandler(self): signal.signal(signal.SIGINT, helper_generic.signal_handler) From c89d86a779be070e1b4e6572c63bef01247d57ac Mon Sep 17 00:00:00 2001 From: f97ada87 Date: Sun, 24 Sep 2017 07:42:15 +1000 Subject: [PATCH 224/407] use getopt parser for command-line arguments --- src/bitmessagemain.py | 33 +++++++++++++++++++++++++++++---- 1 file changed, 29 insertions(+), 4 deletions(-) diff --git a/src/bitmessagemain.py b/src/bitmessagemain.py index b015d5bf..f5793ba5 100755 --- a/src/bitmessagemain.py +++ b/src/bitmessagemain.py @@ -29,6 +29,7 @@ from struct import pack from subprocess import call from time import sleep from random import randint +import getopt from api import MySimpleXMLRPCRequestHandler, StoppableXMLRPCServer from helper_startup import isOurOperatingSystemLimitedToHavingVeryFewHalfOpenConnections @@ -199,11 +200,24 @@ class Main: def start(self, daemon=False): _fixSocket() - shared.daemon = daemon + try: + opts, args = getopt.getopt(sys.argv[1:], "hcd", + ["help", "curses", "daemon"]) - # get curses flag - if '-c' in sys.argv: - state.curses = True + except getopt.GetoptError: + self.usage() + sys.exit(2) + + for opt, arg in opts: + if opt in ("-h", "--help"): + self.usage() + sys.exit() + elif opt in ("-d", "--daemon"): + daemon = True + elif opt in ("-c", "--curses"): + state.curses = True + + shared.daemon = daemon # is the application already running? If yes then exit. shared.thisapp = singleinstance("", daemon) @@ -362,6 +376,17 @@ class Main: signal.signal(signal.SIGTERM, helper_generic.signal_handler) # signal.signal(signal.SIGINT, signal.SIG_DFL) + def usage(self): + print 'Usage: ' + sys.argv[0] + ' [OPTIONS]' + print ''' +Options: + -h, --help show this help message and exit + -c, --curses use curses (text mode) interface + -d, --daemon run in daemon (background) mode + +All parameters are optional. +''' + def stop(self): with shared.printLock: print('Stopping Bitmessage Deamon.') From 849583642874d2144e26c49f629de3dd8f82a66b Mon Sep 17 00:00:00 2001 From: PyBitmessage Translations Date: Sun, 24 Sep 2017 11:49:32 +0200 Subject: [PATCH 225/407] Auto-updated language ru from transifex --- src/translations/bitmessage_ru.qm | Bin 92019 -> 93991 bytes src/translations/bitmessage_ru.ts | 415 +++++++++++++++++------------- 2 files changed, 230 insertions(+), 185 deletions(-) diff --git a/src/translations/bitmessage_ru.qm b/src/translations/bitmessage_ru.qm index f298a70d7e40e8131fe5229ca83b9eb7318c59f5..2cdfe4db1dcb94bd4196bf9f57bcc9f46dcc9db9 100644 GIT binary patch delta 4697 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 delta 3307 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 diff --git a/src/translations/bitmessage_ru.ts b/src/translations/bitmessage_ru.ts index 450b346e..038ddda0 100644 --- a/src/translations/bitmessage_ru.ts +++ b/src/translations/bitmessage_ru.ts @@ -58,12 +58,12 @@ EmailGatewayRegistrationDialog - + Registration failed: Регистрация не удалась: - + The requested email address is not available, please try a new one. Fill out the new desired email address (including @mailchuck.com) below: Запрашиваемый адрес email недоступен, попробуйте ввести другой. Введите желаемый адрес (включая @mailchuck.com) ниже: @@ -168,52 +168,52 @@ Please type the desired email address (including @mailchuck.com) below: MainWindow - + Reply to sender Ответить отправителю - + Reply to channel Ответить в канал - + Add sender to your Address Book Добавить отправителя в адресную книгу - + Add sender to your Blacklist Добавить отправителя в чёрный список - + Move to Trash Поместить в корзину - + Undelete Отменить удаление - + View HTML code as formatted text Просмотреть HTML код как отформатированный текст - + Save message as... Сохранить сообщение как ... - + Mark Unread Отметить как непрочитанное - + New Новый адрес @@ -238,12 +238,12 @@ Please type the desired email address (including @mailchuck.com) below: Скопировать адрес в буфер обмена - + Special address behavior... Особое поведение адресов... - + Email gateway Email-шлюз @@ -253,37 +253,37 @@ Please type the desired email address (including @mailchuck.com) below: Удалить - + Send message to this address Отправить сообщение на этот адрес - + Subscribe to this address Подписаться на рассылку с этого адреса - + Add New Address Добавить новый адрес - + Copy destination address to clipboard Скопировать адрес отправки в буфер обмена - + Force send Форсировать отправку - + One of your addresses, %1, is an old version 1 address. Version 1 addresses are no longer supported. May we delete it now? Один из Ваших адресов, %1, является устаревшим адресом версии 1. Адреса версии 1 больше не поддерживаются. Хотите ли Вы удалить его сейчас? - + Waiting for their encryption key. Will request it again soon. Ожидаем ключ шифрования от Вашего собеседника. Запрос будет повторен через некоторое время. @@ -293,17 +293,17 @@ Please type the desired email address (including @mailchuck.com) below: - + Queued. В очереди. - + Message sent. Waiting for acknowledgement. Sent at %1 Сообщение отправлено. Ожидаем подтверждения. Отправлено в %1 - + Message sent. Sent at %1 Сообщение отправлено в %1 @@ -313,47 +313,47 @@ Please type the desired email address (including @mailchuck.com) below: - + Acknowledgement of the message received %1 Доставлено в %1 - + Broadcast queued. Рассылка ожидает очереди. - + Broadcast on %1 Рассылка на %1 - + Problem: The work demanded by the recipient is more difficult than you are willing to do. %1 Проблема: Ваш получатель требует более сложных вычислений, чем максимум, указанный в Ваших настройках. %1 - + Problem: The recipient's encryption key is no good. Could not encrypt message. %1 Проблема: ключ получателя неправильный. Невозможно зашифровать сообщение. %1 - + Forced difficulty override. Send should start soon. Форсирована смена сложности. Отправляем через некоторое время. - + Unknown status: %1 %2 Неизвестный статус: %1 %2 - + Not Connected Не соединено - + Show Bitmessage Показать Bitmessage @@ -363,12 +363,12 @@ Please type the desired email address (including @mailchuck.com) below: Отправить - + Subscribe Подписки - + Channel Канал @@ -378,13 +378,13 @@ Please type the desired email address (including @mailchuck.com) below: Выйти - + You may manage your keys by editing the keys.dat file stored in the same directory as this program. It is important that you back up this file. Вы можете управлять Вашими ключами, редактируя файл keys.dat, находящийся в той же папке, что и эта программа. Создайте резервную копию этого файла перед тем как будете его редактировать. - + You may manage your keys by editing the keys.dat file stored in %1 It is important that you back up this file. @@ -393,19 +393,19 @@ It is important that you back up this file. Создайте резервную копию этого файла перед тем как будете его редактировать. - + Open keys.dat? Открыть файл keys.dat? - + You may manage your keys by editing the keys.dat file stored in the same directory as this program. It is important that you back up this file. Would you like to open the file now? (Be sure to close Bitmessage before making any changes.) Вы можете управлять Вашими ключами, редактируя файл keys.dat, находящийся в той же папке, что и эта программа. Создайте резервную копию этого файла перед тем как будете его редактировать. Хотели бы Вы открыть этот файл сейчас? (пожалуйста, закройте Bitmessage до того как Вы внесёте в этот файл какие-либо изменения.) - + You may manage your keys by editing the keys.dat file stored in %1 It is important that you back up this file. Would you like to open the file now? (Be sure to close Bitmessage before making any changes.) @@ -415,37 +415,37 @@ It is important that you back up this file. Would you like to open the file now? (пожалуйста, закройте Bitmessage до того как Вы внесёте в этот файл какие-либо изменения.) - + Delete trash? Очистить корзину? - + Are you sure you want to delete all trashed messages? Вы уверены что хотите очистить корзину? - + bad passphrase Неподходящая секретная фраза - + You must type your passphrase. If you don't have one then this is not the form for you. Вы должны ввести секретную фразу. Если Вы не хотите этого делать, то Вы выбрали неправильную опцию. - + Bad address version number Неверный номер версии адреса - + Your address version number must be a number: either 3 or 4. Адрес номера версии должен быть числом: либо 3, либо 4. - + Your address version number must be either 3 or 4. Адрес номера версии должен быть либо 3, либо 4. @@ -515,22 +515,22 @@ It is important that you back up this file. Would you like to open the file now? - + Connection lost Соединение потеряно - + Connected Соединено - + Message trashed Сообщение удалено - + The TTL, or Time-To-Live is the length of time that the network will hold the message. The recipient must get it during this time. If your Bitmessage client does not hear an acknowledgement, it will resend the message automatically. The longer the Time-To-Live, the @@ -541,17 +541,17 @@ It is important that you back up this file. Would you like to open the file now? сообщение. Часто разумным вариантом будет установка TTL на 4 или 5 дней. - + Message too long Сообщение слишком длинное - + The message that you are trying to send is too long by %1 bytes. (The maximum is 261644 bytes). Please cut it down before sending. Сообщение, которое вы пытаетесь отправить, длиннее максимально допустимого на %1 байт. (Максимально допустимое значение 261644 байта). Пожалуйста, сократите сообщение перед отправкой. - + Error: Your account wasn't registered at an email gateway. Sending registration now as %1, please wait for the registration to be processed before retrying sending. Ошибка: ваш аккаунт не зарегистрирован на Email-шлюзе. Отправка регистрации %1, пожалуйста, подождите пока процесс регистрации не завершится, прежде чем попытаться отправить сообщение заново. @@ -596,67 +596,67 @@ It is important that you back up this file. Would you like to open the file now? - + Error: You must specify a From address. If you don't have one, go to the 'Your Identities' tab. Вы должны указать адрес в поле "От кого". Вы можете найти Ваш адрес во вкладке "Ваши Адреса". - + Address version number Версия адреса - + Concerning the address %1, Bitmessage cannot understand address version numbers of %2. Perhaps upgrade Bitmessage to the latest version. По поводу адреса %1: Bitmessage не поддерживаем адреса версии %2. Возможно, Вам нужно обновить клиент Bitmessage. - + Stream number Номер потока - + Concerning the address %1, Bitmessage cannot handle stream numbers of %2. Perhaps upgrade Bitmessage to the latest version. По поводу адреса %1: Bitmessage не поддерживаем стрим номер %2. Возможно, Вам нужно обновить клиент Bitmessage. - + Warning: You are currently not connected. Bitmessage will do the work necessary to send the message but it won't send until you connect. Внимание: Вы не подключены к сети. Bitmessage выполнит работу, требуемую для отправки сообщения, но не отправит его до тех пор, пока Вы не подключитесь. - + Message queued. Сообщение в очереди. - + Your 'To' field is empty. Вы не заполнили поле 'Кому'. - + Right click one or more entries in your address book and select 'Send message to this address'. Нажмите правую кнопку мыши на каком-либо адресе и выберите "Отправить сообщение на этот адрес". - + Fetched address from namecoin identity. Получить адрес через Namecoin. - + New Message Новое сообщение - + From - От + - + Sending email gateway registration request Отправка запроса на регистрацию на Email-шлюзе @@ -671,142 +671,142 @@ It is important that you back up this file. Would you like to open the file now? Вы ввели неправильный адрес. Это адрес проигнорирован. - + Error: You cannot add the same address to your address book twice. Try renaming the existing one if you want. Ошибка: Вы не можете добавлять один и тот же адрес в Адресную Книгу несколько раз. Попробуйте переименовать существующий адрес. - + Error: You cannot add the same address to your subscriptions twice. Perhaps rename the existing one if you want. Ошибка: вы не можете добавить один и тот же адрес в ваши подписки дважды. Пожалуйста, переименуйте имеющийся адрес, если хотите. - + Restart Перезапустить - + You must restart Bitmessage for the port number change to take effect. Вы должны перезапустить Bitmessage, чтобы смена номера порта имела эффект. - + Bitmessage will use your proxy from now on but you may want to manually restart Bitmessage now to close existing connections (if any). Bitmessage будет использовать Ваш прокси, начиная прямо сейчас. Тем не менее Вам имеет смысл перезапустить Bitmessage, чтобы закрыть уже существующие соединения. - + Number needed Требуется число - + Your maximum download and upload rate must be numbers. Ignoring what you typed. Скорости загрузки и выгрузки должны быть числами. Игнорирую то, что вы набрали. - + Will not resend ever Не пересылать никогда - + Note that the time limit you entered is less than the amount of time Bitmessage waits for the first resend attempt therefore your messages will never be resent. Обратите внимание, что лимит времени, который вы ввели, меньше чем время, которое Bitmessage ждет перед первой попыткой переотправки сообщения, поэтому ваши сообщения никогда не будут переотправлены. - + Sending email gateway unregistration request Отправка запроса на отмену регистрации на Email-шлюзе - + Sending email gateway status request Отправка запроса статуса аккаунта на Email-шлюзе - + Passphrase mismatch Секретная фраза не подходит - + The passphrase you entered twice doesn't match. Try again. Вы ввели две разные секретные фразы. Пожалуйста, повторите заново. - + Choose a passphrase Придумайте секретную фразу - + You really do need a passphrase. Вы действительно должны ввести секретную фразу. - + Address is gone Адрес утерян - + Bitmessage cannot find your address %1. Perhaps you removed it? Bitmessage не может найти Ваш адрес %1. Возможно Вы удалили его? - + Address disabled Адрес выключен - + Error: The address from which you are trying to send is disabled. You'll have to enable it on the 'Your Identities' tab before using it. Ошибка: адрес, с которого Вы пытаетесь отправить, выключен. Вам нужно будет включить этот адрес во вкладке "Ваши адреса". - + Entry added to the Address Book. Edit the label to your liking. Запись добавлена в Адресную Книгу. Вы можете её отредактировать. - + Entry added to the blacklist. Edit the label to your liking. Запись добавлена в чёрный список. Измените название по своему вкусу. - + Error: You cannot add the same address to your blacklist twice. Try renaming the existing one if you want. Ошибка: вы не можете добавить один и тот же адрес в чёрный список дважды. Попробуйте переименовать существующий адрес. - + Moved items to trash. Удалено в корзину. - + Undeleted item. Отменить удаление записи - + Save As... Сохранить как ... - + Write error. Ошибка записи. - + No addresses selected. Вы не выбрали адрес. - + If you delete the subscription, messages that you already received will become inaccessible. Maybe you can consider disabling the subscription instead. Disabled subscriptions will not receive new messages, but you can still view messages you already received. Are you sure you want to delete the subscription? @@ -815,7 +815,7 @@ Are you sure you want to delete the subscription? Вы уверены, что хотите отменить подписку? - + If you delete the channel, messages that you already received will become inaccessible. Maybe you can consider disabling the channel instead. Disabled channels will not receive new messages, but you can still view messages you already received. Are you sure you want to delete the channel? @@ -824,92 +824,92 @@ Are you sure you want to delete the channel? Вы уверены, что хотите удалить канал? - + Do you really want to remove this avatar? Вы уверены, что хотите удалить этот аватар? - + You have already set an avatar for this address. Do you really want to overwrite it? У вас уже есть аватар для этого адреса. Вы уверены, что хотите перезаписать аватар? - + Start-on-login not yet supported on your OS. Запуск программы при входе в систему ещё не поддерживается в вашей операционной системе. - + Minimize-to-tray not yet supported on your OS. Сворачивание в трей ещё не поддерживается в вашей операционной системе. - + Tray notifications not yet supported on your OS. Уведомления в трее ещё не поддерживаеются в вашей операционной системе. - + Testing... Проверяем... - + This is a chan address. You cannot use it as a pseudo-mailing list. Это адрес chan-а. Вы не можете его использовать как адрес рассылки. - + The address should start with ''BM-'' Адрес должен начинаться с "BM-" - + The address is not typed or copied correctly (the checksum failed). Адрес введен или скопирован неверно (контрольная сумма не сходится). - + The version number of this address is higher than this software can support. Please upgrade Bitmessage. Версия этого адреса более поздняя, чем Ваша программа. Пожалуйста, обновите программу Bitmessage. - + The address contains invalid characters. Адрес содержит запрещённые символы. - + Some data encoded in the address is too short. Данные, закодированные в адресе, слишком короткие. - + Some data encoded in the address is too long. Данные, закодированные в адресе, слишком длинные. - + Some data encoded in the address is malformed. Данные, закодированные в адресе, имеют неверный формат. - + Enter an address above. Введите адрес выше. - + Address is an old type. We cannot display its past broadcasts. Адрес старого типа. Мы не можем отобразить его прошлые рассылки. - + There are no recent broadcasts from this address to display. Нет недавних рассылок с этого адреса для отображения. - + You are using TCP port %1. (This can be changed in the settings). Вы используете TCP порт %1. (Его можно поменять в настройках). @@ -1119,47 +1119,47 @@ Are you sure you want to delete the channel? Добавить новую запись - + Display the %1 recent broadcast(s) from this address. Показать %1 прошлых рассылок с этого адреса. - + New version of PyBitmessage is available: %1. Download it from https://github.com/Bitmessage/PyBitmessage/releases/latest Доступна новая версия PyBitmessage: %1. Загрузите её: https://github.com/Bitmessage/PyBitmessage/releases/latest - + Waiting for PoW to finish... %1% Ожидание окончания PoW... %1% - + Shutting down Pybitmessage... %1% Завершение PyBitmessage... %1% - + Waiting for objects to be sent... %1% Ожидание отправки объектов... %1% - + Saving settings... %1% Сохранение настроек... %1% - + Shutting down core... %1% Завершение работы ядра... %1% - + Stopping notifications... %1% Остановка сервиса уведомлений... %1% - + Shutdown imminent... %1% Завершение вот-вот произойдет... %1% @@ -1169,17 +1169,17 @@ Are you sure you want to delete the channel? %n час%n часа%n часов%n час(а/ов) - + %n day(s) %n день%n дня%n дней%n дней - + Shutting down PyBitmessage... %1% Завершение PyBitmessage... %1% - + Sent Отправлено @@ -1224,131 +1224,131 @@ Are you sure you want to delete the channel? Внимание: свободное место на диске закончилось. Bitmessage завершит свою работу. - + Error! Could not find sender address (your address) in the keys.dat file. Ошибка: невозможно найти адрес отправителя (ваш адрес) в файле ключей keys.dat - + Doing work necessary to send broadcast... Выполнение работы, требуемой для рассылки... - + Broadcast sent on %1 Рассылка отправлена на %1 - + Encryption key was requested earlier. Ключ шифрования запрошен ранее. - + Sending a request for the recipient's encryption key. Отправка запроса ключа шифрования получателя. - + Looking up the receiver's public key Поиск открытого ключа получателя - + Problem: Destination is a mobile device who requests that the destination be included in the message but this is disallowed in your settings. %1 Проблема: адресат является мобильным устройством, которое требует, чтобы адрес назначения был включен в сообщение, однако, это запрещено в ваших настройках. %1 - + Doing work necessary to send message. There is no required difficulty for version 2 addresses like this. Выполнение работы, требуемой для отправки сообщения. Для адреса версии 2 (как этот), не требуется указание сложности. - + Doing work necessary to send message. Receiver's required difficulty: %1 and %2 Выполнение работы, требуемой для отправки сообщения. Получатель запросил сложность: %1 и %2 - + Problem: The work demanded by the recipient (%1 and %2) is more difficult than you are willing to do. %3 Проблема: сложность, затребованная получателем (%1 и %2) гораздо больше, чем вы готовы сделать. %3 - + Problem: You are trying to send a message to yourself or a chan but your encryption key could not be found in the keys.dat file. Could not encrypt message. %1 Проблема: вы пытаетесь отправить сообщение самому себе или в чан, но ваш ключ шифрования не найден в файле ключей keys.dat. Невозможно зашифровать сообщение. %1 - + Doing work necessary to send message. Выполнение работы, требуемой для отправки сообщения. - + Message sent. Waiting for acknowledgement. Sent on %1 Отправлено. Ожидаем подтверждения. Отправлено в %1 - + Doing work necessary to request encryption key. Выполнение работы, требуемой для запроса ключа шифрования. - + Broadcasting the public key request. This program will auto-retry if they are offline. Рассылка запросов открытого ключа шифрования. Программа будет повторять попытки, если они оффлайн. - + Sending public key request. Waiting for reply. Requested at %1 Отправка запроса открытого ключа шифрования. Ожидание ответа. Запрошено в %1 - + UPnP port mapping established on port %1 Распределение портов UPnP завершилось выделением порта %1 - + UPnP port mapping removed Распределение портов UPnP отменено - + Mark all messages as read Отметить все сообщения как прочтенные - + Are you sure you would like to mark all messages read? Вы уверены, что хотите отметить все сообщения как прочтенные? - + Doing work necessary to send broadcast. Выполнение работы, требуемой для отправки рассылки. - + Proof of work pending Ожидается доказательство работы - + %n object(s) pending proof of work %n объект в ожидании доказательства работы%n объекта в ожидании доказательства работы%n объектов в ожидании доказательства работы%n объектов в ожидании доказательства работы - + %n object(s) waiting to be distributed %n объект ожидает раздачи%n объекта ожидают раздачи%n объектов ожидают раздачи%n объектов ожидают раздачи - + Wait until these tasks finish? Подождать завершения этих задач? @@ -1408,12 +1408,17 @@ Receiver's required difficulty: %1 and %2 Не удалось разобрать ответ NMControl. - + Your GPU(s) did not calculate correctly, disabling OpenCL. Please report to the developers. Ваша видеокарта вычислила неправильно, отключаем OpenCL. Пожалуйста, сообщите разработчикам. - + + Set notification sound... + Установить звук уведомления... + + + Welcome to easy and secure Bitmessage * send messages to other people @@ -1427,85 +1432,100 @@ Receiver's required difficulty: %1 and %2 * участвуйте в обсуждениях в чанах - + not recommended for chans не рекомендовано для чанов - + Problems connecting? Try enabling UPnP in the Network Settings Проблемы подключения? Попробуйте включить UPnP в сетевых настройках. - + + You are trying to send an email instead of a bitmessage. This requires registering with a gateway. Attempt to register? + Вы пытаетесь отправить email вместо bitmessage. Для этого нужно зарегистрироваться на шлюзе. Попробовать зарегистрироваться? + + + Error: Bitmessage addresses start with BM- Please check the recipient address %1 Ошибка: адреса Bitmessage начинаются с "BM-". Пожалуйста, проверьте адрес получателя %1. - + Error: The recipient address %1 is not typed or copied correctly. Please check it. Ошибка: адрес получателя %1 набран или скопирован неправильно. Пожалуйста, проверьте его. - + Error: The recipient address %1 contains invalid characters. Please check it. Ошибка: адрес получателя %1 содержит недопустимые символы. Пожалуйста, проверьте его. - + Error: The version of the recipient address %1 is too high. Either you need to upgrade your Bitmessage software or your acquaintance is being clever. Ошибка: версия адреса получателя %1 слишком высокая. Либо вам нужно обновить программу Bitmessage, либо ваш знакомый - умник. - + Error: Some data encoded in the recipient address %1 is too short. There might be something wrong with the software of your acquaintance. Ошибка: часть данных, закодированных в адресе получателя %1 слишком короткая. Видимо, что-то не так с программой, используемой вашим знакомым. - + Error: Some data encoded in the recipient address %1 is too long. There might be something wrong with the software of your acquaintance. Ошибка: часть данных, закодированных в адресе получателя %1 слишком длинная. Видимо, что-то не так с программой, используемой вашим знакомым. - + Error: Some data encoded in the recipient address %1 is malformed. There might be something wrong with the software of your acquaintance. Ошибка: часть данных, закодированных в адресе получателя %1 сформирована неправильно. Видимо, что-то не так с программой, используемой вашим знакомым. - + Error: Something is wrong with the recipient address %1. Ошибка: что-то не так с адресом получателя %1. - + + From %1 + От %1 + + + Synchronisation pending Ожидается синхронизация - + Bitmessage hasn't synchronised with the network, %n object(s) to be downloaded. If you quit now, it may cause delivery delays. Wait until the synchronisation finishes? Bitmessage не синхронизирован с сетью, незагруженных объектов: %n. Выход сейчас может привести к задержкам доставки. Подождать завершения синхронизации?Bitmessage не синхронизирован с сетью, незагруженных объектов: %n. Выход сейчас может привести к задержкам доставки. Подождать завершения синхронизации?Bitmessage не синхронизирован с сетью, незагруженных объектов: %n. Выход сейчас может привести к задержкам доставки. Подождать завершения синхронизации?Bitmessage не синхронизирован с сетью, незагруженных объектов: %n. Выход сейчас может привести к задержкам доставки. Подождать завершения синхронизации? - + Not connected Не подключено - + Bitmessage isn't connected to the network. If you quit now, it may cause delivery delays. Wait until connected and the synchronisation finishes? Bitmessage не подключен к сети. Выход сейчас может привести к задержкам доставки. Подождать подключения и завершения синхронизации? - + Waiting for network connection... Ожидание сетевого подключения... - + Waiting for finishing synchronisation... Ожидание окончания синхронизации... + + + You have already set a notification sound for this address book entry. Do you really want to overwrite it? + У вас уже есть звук уведомления для этого адресата. Вы уверены, что хотите перезаписать звук уведомления? + MessageView @@ -1848,37 +1868,37 @@ The 'Random Number' option is selected by default but deterministic ad Всего соединений: - + Since startup: С начала работы: - + Processed 0 person-to-person messages. Обработано 0 сообщений. - + Processed 0 public keys. Обработано 0 открытых ключей. - + Processed 0 broadcasts. Обработано 0 рассылок. - + Inventory lookups per second: 0 Поисков в каталоге в секунду: 0 - + Objects to be synced: Несинхронизированные объекты: - + Stream # № потока @@ -1913,12 +1933,12 @@ The 'Random Number' option is selected by default but deterministic ad Поисков в каталоге в секунду: %1 - + Up: 0 kB/s Отправка: 0 кБ/с - + Down: 0 kB/s Загрузка: 0 кБ/с @@ -1958,20 +1978,45 @@ The 'Random Number' option is selected by default but deterministic ad Узел - + + IP address or hostname + Адрес IP или имя узла + + + Rating Рейтинг - + + PyBitmessage tracks the success rate of connection attempts to individual nodes. The rating ranges from -1 to 1 and affects the likelihood of selecting the node in the future + PyBitmessage отслеживает шанс успеха попыток подключения к отдельным узлам. Рейтинг варьируется в диапазоне от -1 до 1 и влияет на вероятность выбора узла в будущем. + + + User agent Название приложения - + + Peer's self-reported software + Название ПО, как сообщает узел + + + TLS TLS + + + Connection encryption + Шифрование соединения + + + + List of streams negotiated between you and the peer + Перечень потоков, согласованных с конкретным узлом + newChanDialog @@ -2067,17 +2112,17 @@ The 'Random Number' option is selected by default but deterministic ad proofofwork - + C PoW module built successfully. Модуль C для PoW успешно собран. - + Failed to build C PoW module. Please build it manually. Не удалось собрать модуль C для PoW. Пожалуйста, соберите его вручную. - + C PoW module unavailable. Please build it. Модуль C для PoW недоступен. Пожалуйста, соберите его. From 6ce86b1d0af296dcd2da27a3461b35668f33b83c Mon Sep 17 00:00:00 2001 From: Peter Surda Date: Mon, 25 Sep 2017 01:17:04 +0200 Subject: [PATCH 226/407] Dandelion++ implementation - untested, some functionality may be missing, don't turn on - also, it randomises upload of requested objects - affects #1049 --- src/bitmessagemain.py | 2 ++ src/bmconfigparser.py | 1 + src/class_singleCleaner.py | 5 ++++ src/network/bmobject.py | 4 +++ src/network/bmproto.py | 52 +++++++++++++++++++++++++++++++---- src/network/connectionpool.py | 16 +++++++++++ src/network/dandelion.py | 29 +++++++++++++++++++ src/network/invthread.py | 3 ++ src/network/objectracker.py | 6 ++++ src/network/tcp.py | 6 +++- src/protocol.py | 15 ++++++++-- 11 files changed, 131 insertions(+), 8 deletions(-) create mode 100644 src/network/dandelion.py diff --git a/src/bitmessagemain.py b/src/bitmessagemain.py index f5793ba5..1a091c4c 100755 --- a/src/bitmessagemain.py +++ b/src/bitmessagemain.py @@ -54,6 +54,7 @@ from bmconfigparser import BMConfigParser from inventory import Inventory from network.connectionpool import BMConnectionPool +from network.dandelion import DandelionStems from network.networkthread import BMNetworkThread from network.receivequeuethread import ReceiveQueueThread from network.announcethread import AnnounceThread @@ -248,6 +249,7 @@ class Main: sqlLookup.start() Inventory() # init + DandelionStems() # init, needs to be early because other thread may access it early # SMTP delivery thread if daemon and BMConfigParser().safeGet("bitmessagesettings", "smtpdeliver", '') != '': diff --git a/src/bmconfigparser.py b/src/bmconfigparser.py index acc4476a..bb4377a2 100644 --- a/src/bmconfigparser.py +++ b/src/bmconfigparser.py @@ -20,6 +20,7 @@ BMConfigDefaults = { }, "network": { "bind": '', + "dandelion": 0, }, "inventory": { "storage": "sqlite", diff --git a/src/class_singleCleaner.py b/src/class_singleCleaner.py index c37c3ecf..2e4140b6 100644 --- a/src/class_singleCleaner.py +++ b/src/class_singleCleaner.py @@ -10,6 +10,7 @@ from helper_sql import * from helper_threading import * from inventory import Inventory from network.connectionpool import BMConnectionPool +from network.dandelion import DandelionStems from debug import logger import knownnodes import queues @@ -126,6 +127,10 @@ class singleCleaner(threading.Thread, StoppableThread): # inv/object tracking for connection in BMConnectionPool().inboundConnections.values() + BMConnectionPool().outboundConnections.values(): connection.clean() + # dandelion fluff trigger by expiration + for h, t in DandelionStems().timeouts: + if time.time() > t: + DandelionStems().remove(h) # discovery tracking exp = time.time() - singleCleaner.expireDiscoveredPeers diff --git a/src/network/bmobject.py b/src/network/bmobject.py index 98a14b5e..4cde0c4f 100644 --- a/src/network/bmobject.py +++ b/src/network/bmobject.py @@ -4,6 +4,7 @@ import time from addresses import calculateInventoryHash from debug import logger from inventory import Inventory +from network.dandelion import DandelionStems import protocol import state @@ -66,6 +67,9 @@ class BMObject(object): raise BMObjectUnwantedStreamError() def checkAlreadyHave(self): + # if it's a stem duplicate, pretend we don't have it + if self.inventoryHash in DandelionStems().stem: + return if self.inventoryHash in Inventory(): raise BMObjectAlreadyHaveError() diff --git a/src/network/bmproto.py b/src/network/bmproto.py index dbdc26d2..d5214471 100644 --- a/src/network/bmproto.py +++ b/src/network/bmproto.py @@ -1,6 +1,7 @@ import base64 import hashlib import time +import random import socket import struct @@ -9,6 +10,7 @@ from debug import logger from inventory import Inventory import knownnodes from network.advanceddispatcher import AdvancedDispatcher +from network.dandelion import DandelionStems, REASSIGN_INTERVAL from network.bmobject import BMObject, BMObjectInsufficientPOWError, BMObjectInvalidDataError, \ BMObjectExpiredError, BMObjectUnwantedStreamError, BMObjectInvalidError, BMObjectAlreadyHaveError import network.connectionpool @@ -61,6 +63,8 @@ class BMProto(AdvancedDispatcher, ObjectTracker): self.payloadOffset = 0 self.expectBytes = protocol.Header.size self.object = None + self.dandelionRoutes = [] + self.dandelionRefresh = 0 def state_bm_header(self): self.magic, self.command, self.payloadLength, self.checksum = protocol.Header.unpack(self.read_buf[:protocol.Header.size]) @@ -266,13 +270,23 @@ class BMProto(AdvancedDispatcher, ObjectTracker): # skip? if time.time() < self.skipUntil: return True - #TODO make this more asynchronous and allow reordering + #TODO make this more asynchronous + random.shuffle(items) for i in items: - try: - self.append_write_buf(protocol.CreatePacket('object', Inventory()[i].payload)) - except KeyError: + if i in DandelionStems().stem and \ + self not in DandelionStems().stem[i]: self.antiIntersectionDelay() - logger.info('%s asked for an object we don\'t have.', self.destination) + logger.info('%s asked for a stem object we didn\'t offer to it.', self.destination) + break + else: + try: + self.append_write_buf(protocol.CreatePacket('object', Inventory()[i].payload)) + except KeyError: + self.antiIntersectionDelay() + logger.info('%s asked for an object we don\'t have.', self.destination) + break + # I think that aborting after the first missing/stem object is more secure + # when using random reordering, as the recipient won't know exactly which objects we refuse to deliver return True def bm_command_inv(self): @@ -289,6 +303,34 @@ class BMProto(AdvancedDispatcher, ObjectTracker): return True + def bm_command_dinv(self): + """ + Dandelion stem announce + """ + items = self.decode_payload_content("l32s") + + if len(items) >= BMProto.maxObjectCount: + logger.error("Too many items in dinv message!") + raise BMProtoExcessiveDataError() + else: + pass + + # ignore command if dandelion turned off + if BMConfigParser().safeGetBoolean("network", "dandelion") == 0: + return True + + if self.dandelionRefresh < time.time(): + self.dandelionRoutes = network.connectionpool.dandelionRouteSelector(self) + self.dandelionRefresh = time.time() + REASSIGN_INTERVAL + + for i in items: + # Fluff trigger by RNG, per item + if random.randint(1, 100) < BMConfigParser().safeGetBoolean("network", "dandelion"): + DandelionStem().add(i, self.dandelionRoutes) + self.handleReceivedInventory(i) + + return True + def bm_command_object(self): objectOffset = self.payloadOffset nonce, expiresTime, objectType, version, streamNumber = self.decode_payload_content("QQIvv") diff --git a/src/network/connectionpool.py b/src/network/connectionpool.py index 3a7f9e6d..cde5c9eb 100644 --- a/src/network/connectionpool.py +++ b/src/network/connectionpool.py @@ -51,6 +51,22 @@ class BMConnectionPool(object): except KeyError: pass + def dandelionRouteSelector(node): + # Choose 2 peers randomly + # TODO: handle streams + peers = [] + connections = BMConnectionPool().inboundConnections.values() + \ + BMConnectionPool().outboundConnections.values() + random.shuffle(connections) + for i in connections: + if i == node: + continue + if i.services | protocol.NODE_DANDELION: + peers.append(i) + if len(peers) == 2: + break + return peers + def connectToStream(self, streamNumber): self.streams.append(streamNumber) diff --git a/src/network/dandelion.py b/src/network/dandelion.py new file mode 100644 index 00000000..ea27915f --- /dev/null +++ b/src/network/dandelion.py @@ -0,0 +1,29 @@ +import random +from threading import RLock + +import protocol +from singleton import Singleton + +# randomise routes after 600 seconds +REASSIGN_INTERVAL = 600 +FLUFF_TRIGGER_TIMEOUT = 300 + +@Singleton +class DandelionStems(): + def __init__(self): + self.stem = {} + self.timeouts = {} + self.lock = RLock() + + def add(self, hashId, stems): + with self.lock: + self.stem[hashId] = stems + self.timeouts[hashId] = time.time() + + def remove(self, hashId): + with self.lock: + try: + del self.stem[hashId] + del self.timeouts[hashId] + except KeyError: + pass diff --git a/src/network/invthread.py b/src/network/invthread.py index d680ea13..a868ce95 100644 --- a/src/network/invthread.py +++ b/src/network/invthread.py @@ -4,6 +4,7 @@ import threading import addresses from helper_threading import StoppableThread from network.connectionpool import BMConnectionPool +from network.dandelion import DandelionStems from queues import invQueue import protocol import state @@ -39,6 +40,8 @@ class InvThread(threading.Thread, StoppableThread): for inv in chunk: if inv[0] not in connection.streams: continue + if inv in DandelionStems().stem and connection not in DandelionStems().stem[inv]: + continue try: with connection.objectsNewToThemLock: del connection.objectsNewToThem[inv[1]] diff --git a/src/network/objectracker.py b/src/network/objectracker.py index 4541ea76..7149f4b1 100644 --- a/src/network/objectracker.py +++ b/src/network/objectracker.py @@ -4,6 +4,7 @@ from threading import RLock from debug import logger from inventory import Inventory +from network.dandelion import DandelionStems haveBloom = False @@ -83,6 +84,11 @@ class ObjectTracker(object): if hashId not in Inventory(): with self.objectsNewToMeLock: self.objectsNewToMe[hashId] = True + elif hashId in DandelionStems().stem: + # Fluff trigger by cycle detection + DandelionStems().remove(hashId) + with self.objectsNewToMeLock: + self.objectsNewToMe[hashId] = True def hasAddr(self, addr): if haveBloom: diff --git a/src/network/tcp.py b/src/network/tcp.py index 0fcbc160..60acb22c 100644 --- a/src/network/tcp.py +++ b/src/network/tcp.py @@ -18,6 +18,7 @@ from network.advanceddispatcher import AdvancedDispatcher from network.bmproto import BMProtoError, BMProtoInsufficientDataError, BMProtoExcessiveDataError, BMProto from network.bmobject import BMObject, BMObjectInsufficientPOWError, BMObjectInvalidDataError, BMObjectExpiredError, BMObjectUnwantedStreamError, BMObjectInvalidError, BMObjectAlreadyHaveError import network.connectionpool +from network.dandelion import DandelionStems from network.node import Node import network.asyncore_pollchoose as asyncore from network.proxy import Proxy, ProxyError, GeneralProxyError @@ -88,7 +89,7 @@ class TCPConnection(BMProto, TLSDispatcher): if self.skipUntil > time.time(): logger.debug("Initial skipping processing getdata for %.2fs", self.skipUntil - time.time()) else: - logger.debug("Skipping processing getdata due to missing object for %.2fs", self.skipUntil - time.time()) + logger.debug("Skipping processing getdata due to missing object for %.2fs", delay) self.skipUntil = time.time() + delay def state_connection_fully_established(self): @@ -165,6 +166,9 @@ class TCPConnection(BMProto, TLSDispatcher): # may lock for a long time, but I think it's better than thousands of small locks with self.objectsNewToThemLock: for objHash in Inventory().unexpired_hashes_by_stream(stream): + # don't advertise stem objects on bigInv + if objHash in DandelionStems().stem: + continue bigInvList[objHash] = 0 self.objectsNewToThem[objHash] = time.time() objectCount = 0 diff --git a/src/protocol.py b/src/protocol.py index 7ad0db17..ef31b6c1 100644 --- a/src/protocol.py +++ b/src/protocol.py @@ -23,6 +23,7 @@ from version import softwareVersion #Service flags NODE_NETWORK = 1 NODE_SSL = 2 +NODE_DANDELION = 8 #Bitfield flags BITFIELD_DOESACK = 1 @@ -191,7 +192,12 @@ def CreatePacket(command, payload=''): def assembleVersionMessage(remoteHost, remotePort, participatingStreams, server = False, nodeid = None): payload = '' payload += pack('>L', 3) # protocol version. - payload += pack('>q', NODE_NETWORK|(NODE_SSL if haveSSL(server) else 0)) # bitflags of the services I offer. + # bitflags of the services I offer. + payload += pack('>q', + NODE_NETWORK | + (NODE_SSL if haveSSL(server) else 0) | + (NODE_DANDELION if BMConfigParser().safeGetInt('network', 'dandelion') > 0 else 0) + ) payload += pack('>q', int(time.time())) payload += pack( @@ -203,7 +209,12 @@ def assembleVersionMessage(remoteHost, remotePort, participatingStreams, server payload += encodeHost(remoteHost) payload += pack('>H', remotePort) # remote IPv6 and port - payload += pack('>q', NODE_NETWORK|(NODE_SSL if haveSSL(server) else 0)) # bitflags of the services I offer. + # bitflags of the services I offer. + payload += pack('>q', + NODE_NETWORK | + (NODE_SSL if haveSSL(server) else 0) | + (NODE_DANDELION if BMConfigParser().safeGetInt('network', 'dandelion') > 0 else 0) + ) payload += '\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xFF\xFF' + pack( '>L', 2130706433) # = 127.0.0.1. This will be ignored by the remote host. The actual remote connected IP will be used. # we have a separate extPort and From d574b167d8be0d463ab30918591f8a4b67e60920 Mon Sep 17 00:00:00 2001 From: Peter Surda Date: Mon, 25 Sep 2017 08:49:21 +0200 Subject: [PATCH 227/407] Dandelion updates & fixes - Addresses #1049 - Add dandelion routes for locally generated objects - Minor bugfixes - Send dinv commands on stem objects (instead of always sending inv command) --- src/network/bmproto.py | 2 +- src/network/connectionpool.py | 6 +++--- src/network/invthread.py | 37 +++++++++++++++++++++++++++++------ 3 files changed, 35 insertions(+), 10 deletions(-) diff --git a/src/network/bmproto.py b/src/network/bmproto.py index d5214471..66f066ef 100644 --- a/src/network/bmproto.py +++ b/src/network/bmproto.py @@ -320,7 +320,7 @@ class BMProto(AdvancedDispatcher, ObjectTracker): return True if self.dandelionRefresh < time.time(): - self.dandelionRoutes = network.connectionpool.dandelionRouteSelector(self) + self.dandelionRoutes = BMConnectionPool.dandelionRouteSelector(self) self.dandelionRefresh = time.time() + REASSIGN_INTERVAL for i in items: diff --git a/src/network/connectionpool.py b/src/network/connectionpool.py index cde5c9eb..fae509c7 100644 --- a/src/network/connectionpool.py +++ b/src/network/connectionpool.py @@ -51,12 +51,12 @@ class BMConnectionPool(object): except KeyError: pass - def dandelionRouteSelector(node): + def dandelionRouteSelector(self, node): # Choose 2 peers randomly # TODO: handle streams peers = [] - connections = BMConnectionPool().inboundConnections.values() + \ - BMConnectionPool().outboundConnections.values() + connections = self.inboundConnections.values() + \ + self.outboundConnections.values() random.shuffle(connections) for i in connections: if i == node: diff --git a/src/network/invthread.py b/src/network/invthread.py index a868ce95..992930db 100644 --- a/src/network/invthread.py +++ b/src/network/invthread.py @@ -1,10 +1,13 @@ import Queue +from random import randint import threading +from time import time import addresses +from bmconfigparser import BMConfigParser from helper_threading import StoppableThread from network.connectionpool import BMConnectionPool -from network.dandelion import DandelionStems +from network.dandelion import DandelionStems, REASSIGN_INTERVAL from queues import invQueue import protocol import state @@ -14,15 +17,30 @@ class InvThread(threading.Thread, StoppableThread): threading.Thread.__init__(self, name="InvBroadcaster") self.initStop() self.name = "InvBroadcaster" + # for locally generated objects + self.dandelionRoutes = [] + self.dandelionRefresh = 0 + + def dandelionLocalRouteRefresh(self): + if self.dandelionRefresh < time(): + self.dandelionRoutes = BMConnectionPool().dandelionRouteSelector(None) + self.dandelionRefresh = time() + REASSIGN_INTERVAL def run(self): while not state.shutdown: chunk = [] while True: + self.dandelionLocalRouteRefresh() try: data = invQueue.get(False) + # locally generated if len(data) == 2: BMConnectionPool().handleReceivedObject(data[0], data[1]) + # Fluff trigger by RNG + # auto-ignore if config set to 0, i.e. dandelion is off + if randint(1, 100) < BMConfigParser().safeGetBoolean("network", "dandelion"): + DandelionStems.add(data[1], self.dandelionRoutes) + # came over the network else: source = BMConnectionPool().getConnectionByAddr(data[2]) BMConnectionPool().handleReceivedObject(data[0], data[1], source) @@ -36,20 +54,27 @@ class InvThread(threading.Thread, StoppableThread): if chunk: for connection in BMConnectionPool().inboundConnections.values() + \ BMConnectionPool().outboundConnections.values(): - hashes = [] + fluffs = [] + stems = [] for inv in chunk: if inv[0] not in connection.streams: continue - if inv in DandelionStems().stem and connection not in DandelionStems().stem[inv]: + if inv[1] in DandelionStems().stem: + if connection in DandelionStems().stem[inv[1]]: + stems.append(inv[1]) continue + # else try: with connection.objectsNewToThemLock: del connection.objectsNewToThem[inv[1]] - hashes.append(inv[1]) + fluffs.append(inv[1]) except KeyError: continue - if hashes: + if fluffs: connection.append_write_buf(protocol.CreatePacket('inv', \ - addresses.encodeVarint(len(hashes)) + "".join(hashes))) + addresses.encodeVarint(len(fluffs)) + "".join(fluffs))) + if stems: + connection.append_write_buf(protocol.CreatePacket('dinv', \ + addresses.encodeVarint(len(stems)) + "".join(stems))) invQueue.iterate() self.stop.wait(1) From 9923d288e03a24215e3cc90a7e95abbdfa6cdd1f Mon Sep 17 00:00:00 2001 From: Peter Surda Date: Mon, 25 Sep 2017 09:17:15 +0200 Subject: [PATCH 228/407] Dandelion fixes - in route selector, some connections may not have the services attribute (yet) - Addresses #1049 --- src/network/connectionpool.py | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/src/network/connectionpool.py b/src/network/connectionpool.py index fae509c7..2943200b 100644 --- a/src/network/connectionpool.py +++ b/src/network/connectionpool.py @@ -61,10 +61,13 @@ class BMConnectionPool(object): for i in connections: if i == node: continue - if i.services | protocol.NODE_DANDELION: - peers.append(i) - if len(peers) == 2: - break + try: + if i.services | protocol.NODE_DANDELION: + peers.append(i) + if len(peers) == 2: + break + except AttributeError: + continue return peers def connectToStream(self, streamNumber): From 1798c90622d68630b492fa13238703770a29ec29 Mon Sep 17 00:00:00 2001 From: PyBitmessage Translations Date: Mon, 25 Sep 2017 11:10:16 +0200 Subject: [PATCH 229/407] Auto-updated language ja from transifex --- src/translations/bitmessage_ja.qm | Bin 66676 -> 66995 bytes src/translations/bitmessage_ja.ts | 323 ++++++++++++++++-------------- 2 files changed, 169 insertions(+), 154 deletions(-) diff --git a/src/translations/bitmessage_ja.qm b/src/translations/bitmessage_ja.qm index 4cf32fd716f80eb8c6aeb03193bef46811d9b2b8..4a639cbf8891e2567c3d6cf1a334c605391371df 100644 GIT binary patch delta 3535 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 OmyqjM*06GA@_zxJdQ1-h delta 3359 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 EmailGatewayRegistrationDialog - + Registration failed: 登録に失敗しました: - + The requested email address is not available, please try a new one. Fill out the new desired email address (including @mailchuck.com) below: リクエストしたメールアドレスは利用できません。新しいメールアドレスをお試しください。 新しい希望メールアドレス (@mailchuck.com を含む) を次のように記入してください: @@ -168,52 +168,52 @@ Please type the desired email address (including @mailchuck.com) below: MainWindow - + Reply to sender 送信元に返信 - + Reply to channel チャンネルに返信 - + Add sender to your Address Book 送信元をアドレス帳に追加 - + Add sender to your Blacklist 送信元をブラックリストに追加 - + Move to Trash ゴミ箱へ移動 - + Undelete 削除を元に戻す - + View HTML code as formatted text HTMLコードを整形したテキストで表示 - + Save message as... 形式を選択してメッセージを保存 - + Mark Unread 未読にする - + New 新規 @@ -238,12 +238,12 @@ Please type the desired email address (including @mailchuck.com) below: アドレスをコピー - + Special address behavior... アドレスの特別な動作 - + Email gateway メールゲートウェイ @@ -253,37 +253,37 @@ Please type the desired email address (including @mailchuck.com) below: 削除 - + Send message to this address このアドレスへ送信 - + Subscribe to this address このアドレスを購読 - + Add New Address アドレスを追加 - + Copy destination address to clipboard 宛先アドレスをコピー - + Force send 強制的に送信 - + One of your addresses, %1, is an old version 1 address. Version 1 addresses are no longer supported. May we delete it now? %1は古いバージョン1のアドレスです。バージョン1のアドレスはサポートが終了しています。すぐに削除しますか? - + Waiting for their encryption key. Will request it again soon. 暗号鍵を待っています。 すぐにもう一度リクエストします。 @@ -293,17 +293,17 @@ Please type the desired email address (including @mailchuck.com) below: - + Queued. キューに入りました。 - + Message sent. Waiting for acknowledgement. Sent at %1 メッセージを送信しました。 確認応答を待っています。 %1 で送信されました - + Message sent. Sent at %1 メッセージは送信されました。送信先: %1 @@ -313,47 +313,47 @@ Please type the desired email address (including @mailchuck.com) below: - + Acknowledgement of the message received %1 メッセージの確認を受け取りました %1 - + Broadcast queued. 配信がキューに入りました。 - + Broadcast on %1 配信: %1 - + Problem: The work demanded by the recipient is more difficult than you are willing to do. %1 問題: 受信者が要求している処理は現在あなたが設定しているよりも高い難易度です。 %1 - + Problem: The recipient's encryption key is no good. Could not encrypt message. %1 問題: 受信者の暗号鍵は正当でない物です。メッセージを暗号化できません。 %1 - + Forced difficulty override. Send should start soon. 難易度を強制上書きしました。まもなく送信されます。 - + Unknown status: %1 %2 不明なステータス: %1 %2 - + Not Connected 未接続 - + Show Bitmessage Bitmessageを表示 @@ -363,12 +363,12 @@ Please type the desired email address (including @mailchuck.com) below: 送る - + Subscribe 購読 - + Channel チャンネル @@ -378,66 +378,66 @@ Please type the desired email address (including @mailchuck.com) below: 終了 - + You may manage your keys by editing the keys.dat file stored in the same directory as this program. It is important that you back up this file. プログラムを同じディレクトリに保存されているkeys.datファイルを編集することで鍵を管理できます。ファイルをバックアップしておくことも重要です。 - + You may manage your keys by editing the keys.dat file stored in %1 It is important that you back up this file. %1に保存されているkeys.datファイルを編集することで鍵を管理できます。ファイルをバックアップしておくことも重要です。 - + Open keys.dat? keys.datを開きますか? - + You may manage your keys by editing the keys.dat file stored in the same directory as this program. It is important that you back up this file. Would you like to open the file now? (Be sure to close Bitmessage before making any changes.) プログラムを同じディレクトリに保存されているkeys.datファイルを編集することで鍵を管理できます。ファイルをバックアップしておくことも重要です。すぐにファイルを開きますか?(必ず編集する前にBitmessageを終了してください) - + You may manage your keys by editing the keys.dat file stored in %1 It is important that you back up this file. Would you like to open the file now? (Be sure to close Bitmessage before making any changes.) %1に保存されているkeys.datファイルを編集することで鍵を管理できます。ファイルをバックアップしておくことも重要です。すぐにファイルを開きますか?(必ず編集する前にBitmessageを終了してください) - + Delete trash? ゴミ箱を空にしますか? - + Are you sure you want to delete all trashed messages? ゴミ箱内のメッセージを全て削除してもよろしいですか? - + bad passphrase 不正なパスフレーズ - + You must type your passphrase. If you don't have one then this is not the form for you. パスフレーズを入力してください。パスフレーズがない場合は入力する必要はありません。 - + Bad address version number 不正なアドレスのバージョン番号 - + Your address version number must be a number: either 3 or 4. アドレスのバージョン番号は数字にする必要があります: 3 または 4。 - + Your address version number must be either 3 or 4. アドレスのバージョン番号は、3 または 4 のどちらかにする必要があります。 @@ -507,22 +507,22 @@ It is important that you back up this file. Would you like to open the file now? - + Connection lost 接続が切断されました - + Connected 接続済み - + Message trashed メッセージが削除されました - + The TTL, or Time-To-Live is the length of time that the network will hold the message. The recipient must get it during this time. If your Bitmessage client does not hear an acknowledgement, it will resend the message automatically. The longer the Time-To-Live, the @@ -533,17 +533,17 @@ It is important that you back up this file. Would you like to open the file now? コンピュータがメッセージを送信するために必要な処理が増えます。 多くの場合 4〜5 日のTTL(Time-To-Live)が適切です。 - + Message too long メッセージが長すぎます - + The message that you are trying to send is too long by %1 bytes. (The maximum is 261644 bytes). Please cut it down before sending. 送信しようとしているメッセージが %1 バイト長すぎます。 (最大は261644バイトです)。 送信する前に短くしてください。 - + Error: Your account wasn't registered at an email gateway. Sending registration now as %1, please wait for the registration to be processed before retrying sending. エラー: アカウントがメールゲートウェイに登録されていません。 今 %1 として登録を送信しています。送信を再試行する前に、登録が処理されるまでお待ちください。 @@ -588,67 +588,67 @@ It is important that you back up this file. Would you like to open the file now? - + Error: You must specify a From address. If you don't have one, go to the 'Your Identities' tab. エラー: 送信元アドレスを指定してください。まだ作成していない場合には「アドレス一覧」のタブを開いてください。 - + Address version number アドレスのバージョン番号 - + Concerning the address %1, Bitmessage cannot understand address version numbers of %2. Perhaps upgrade Bitmessage to the latest version. アドレス %1 に接続。%2 のバージョン番号は処理できません。Bitmessageを最新のバージョンへアップデートしてください。 - + Stream number ストリーム番号 - + Concerning the address %1, Bitmessage cannot handle stream numbers of %2. Perhaps upgrade Bitmessage to the latest version. アドレス %1 に接続。%2 のストリーム番号は処理できません。Bitmessageを最新のバージョンへアップデートしてください。 - + Warning: You are currently not connected. Bitmessage will do the work necessary to send the message but it won't send until you connect. 警告: 接続されていません。Bitmessageはメッセージの処理を行いますが、ネットワークに接続するまで送信はされません。 - + Message queued. メッセージがキューに入りました。 - + Your 'To' field is empty. 宛先が指定されていません。 - + Right click one or more entries in your address book and select 'Send message to this address'. アドレス帳から一つ、または複数のアドレスを右クリックして「このアドレスへ送信」を選んでください。 - + Fetched address from namecoin identity. namecoin IDからアドレスを取得。 - + New Message 新規メッセージ - + From - 送信元 + - + Sending email gateway registration request メールゲートウェイの登録リクエストを送信しています @@ -663,142 +663,142 @@ It is important that you back up this file. Would you like to open the file now? 入力されたアドレスは不正です。無視されました。 - + Error: You cannot add the same address to your address book twice. Try renaming the existing one if you want. エラー: 同じアドレスを複数アドレス帳に追加する事はできません。既存の項目をリネームしてください。 - + Error: You cannot add the same address to your subscriptions twice. Perhaps rename the existing one if you want. エラー: 購読に、同じアドレスを2回追加することはできません。 必要に応じて、既存の名前を変更してください。 - + Restart 再開 - + You must restart Bitmessage for the port number change to take effect. ポート番号の変更を有効にするにはBitmessageを再起動してください。 - + Bitmessage will use your proxy from now on but you may want to manually restart Bitmessage now to close existing connections (if any). プロキシの設定を有効にするには手動でBitmessageを再起動してください。既に接続がある場合は切断されます。 - + Number needed 数字が必要です - + Your maximum download and upload rate must be numbers. Ignoring what you typed. 最大ダウンロード数とアップロード数は数字にする必要があります。 入力されたものを無視します。 - + Will not resend ever 今後再送信されません - + Note that the time limit you entered is less than the amount of time Bitmessage waits for the first resend attempt therefore your messages will never be resent. 入力した時間制限は、Bitmessageが最初の再送試行を待つ時間よりも短いため、メッセージは再送信されないことにご注意ください。 - + Sending email gateway unregistration request メールゲートウェイの登録抹消リクエストを送信しています - + Sending email gateway status request メールゲートウェイの状態リクエストを送信しています - + Passphrase mismatch パスフレーズが一致しません - + The passphrase you entered twice doesn't match. Try again. 再度入力されたパスフレーズが一致しません。再入力してください。 - + Choose a passphrase パスフレーズを選択してください - + You really do need a passphrase. パスフレーズが必要です。 - + Address is gone アドレスが無効になりました - + Bitmessage cannot find your address %1. Perhaps you removed it? アドレス %1 が見つかりません。既に削除していませんか? - + Address disabled アドレスが無効になりました - + Error: The address from which you are trying to send is disabled. You'll have to enable it on the 'Your Identities' tab before using it. エラー: 送信しようとしたアドレスは無効になっています。使用する前に「アドレス一覧」で有効にしてください。 - + Entry added to the Address Book. Edit the label to your liking. アドレス帳に項目が追加されました。ラベルは自由に編集できます。 - + Entry added to the blacklist. Edit the label to your liking. ブラックリストに項目が追加されました。ラベルは自由に編集できます。 - + Error: You cannot add the same address to your blacklist twice. Try renaming the existing one if you want. エラー: ブラックリストに同じアドレスを2回追加することはできません。 必要に応じて既存の名前を変更してみてください。 - + Moved items to trash. アイテムをゴミ箱へ移動。 - + Undeleted item. アイテムの削除を元に戻します。 - + Save As... 形式を選択して保存 - + Write error. 書き込みエラー。 - + No addresses selected. アドレスが未選択です。 - + If you delete the subscription, messages that you already received will become inaccessible. Maybe you can consider disabling the subscription instead. Disabled subscriptions will not receive new messages, but you can still view messages you already received. Are you sure you want to delete the subscription? @@ -807,7 +807,7 @@ Are you sure you want to delete the subscription? 購読を削除してもよろしいですか? - + If you delete the channel, messages that you already received will become inaccessible. Maybe you can consider disabling the channel instead. Disabled channels will not receive new messages, but you can still view messages you already received. Are you sure you want to delete the channel? @@ -816,92 +816,92 @@ Are you sure you want to delete the channel? チャンネルを削除してもよろしいですか? - + Do you really want to remove this avatar? このアバターを削除してもよろしいですか? - + You have already set an avatar for this address. Do you really want to overwrite it? すでにこのアドレスのアバターを設定しています。 上書きしてもよろしいですか? - + Start-on-login not yet supported on your OS. ログイン時に開始は、まだお使いのOSでサポートされていません。 - + Minimize-to-tray not yet supported on your OS. トレイに最小化は、まだお使いのOSでサポートされていません。 - + Tray notifications not yet supported on your OS. トレイ通知は、まだお使いのOSでサポートされていません。 - + Testing... テスト中 - + This is a chan address. You cannot use it as a pseudo-mailing list. chanアドレスは仮想メーリングリストのアドレスには使用できません。 - + The address should start with ''BM-'' アドレスは「BM-」から始まります - + The address is not typed or copied correctly (the checksum failed). このアドレスは正しく入力、またはコピーされていません。(チェックサムが一致しません)。 - + The version number of this address is higher than this software can support. Please upgrade Bitmessage. このアドレスのバージョン番号はこのプログラムのサポート範囲外です。Bitmessageをアップデートしてください。 - + The address contains invalid characters. 入力されたアドレスは不正な文字を含んでいます。 - + Some data encoded in the address is too short. このアドレスでエンコードされたデータが短すぎます。 - + Some data encoded in the address is too long. このアドレスでエンコードされたデータが長過ぎます。 - + Some data encoded in the address is malformed. このアドレスでエンコードされた一部のデータが不正です。 - + Enter an address above. 上にアドレスを入力してください。 - + Address is an old type. We cannot display its past broadcasts. アドレスが古い形式です。 過去の配信は表示できません。 - + There are no recent broadcasts from this address to display. このアドレスから表示する最近の配信はありません。 - + You are using TCP port %1. (This can be changed in the settings). 使用中のポート %1 (設定で変更できます)。 @@ -1111,47 +1111,47 @@ Are you sure you want to delete the channel? 新しい項目を追加 - + Display the %1 recent broadcast(s) from this address. このアドレスから%1の最新の配信を表示します。 - + New version of PyBitmessage is available: %1. Download it from https://github.com/Bitmessage/PyBitmessage/releases/latest 新しいバージョンの PyBitmessage が利用可能です: %1。 https://github.com/Bitmessage/PyBitmessage/releases/latest からダウンロードしてください - + Waiting for PoW to finish... %1% PoW(証明)が完了するのを待っています... %1% - + Shutting down Pybitmessage... %1% Pybitmessageをシャットダウンしています... %1% - + Waiting for objects to be sent... %1% オブジェクトの送信待ち... %1% - + Saving settings... %1% 設定を保存しています... %1% - + Shutting down core... %1% コアをシャットダウンしています... %1% - + Stopping notifications... %1% 通知を停止しています... %1% - + Shutdown imminent... %1% すぐにシャットダウンします... %1% @@ -1161,17 +1161,17 @@ Are you sure you want to delete the channel? %n 時間 - + %n day(s) %n 日 - + Shutting down PyBitmessage... %1% PyBitmessageをシャットダウンしています... %1% - + Sent 送信済 @@ -1275,7 +1275,7 @@ Receiver's required difficulty: %1 and %2 問題: あなた自身またはチャンネルにメッセージを送信しようとしていますが、暗号鍵がkeys.datファイルに見つかりませんでした。 メッセージを暗号化できませんでした。 %1 - + Doing work necessary to send message. メッセージの送信に必要な処理を行っています。 @@ -1285,7 +1285,7 @@ Receiver's required difficulty: %1 and %2 メッセージを送信しました。 確認応答を待っています。 %1 で送信しました - + Doing work necessary to request encryption key. 暗号鍵のリクエストに必要な処理を行っています。 @@ -1310,37 +1310,37 @@ Receiver's required difficulty: %1 and %2 UPnPポートマッピングを削除しました - + Mark all messages as read すべてのメッセージを既読にする - + Are you sure you would like to mark all messages read? すべてのメッセージを既読にしてもよろしいですか? - + Doing work necessary to send broadcast. 配信に必要な処理を行っています。 - + Proof of work pending PoW(証明)を待っています - + %n object(s) pending proof of work %n オブジェクトが証明待ち (PoW) - + %n object(s) waiting to be distributed %n オブジェクトが配布待ち - + Wait until these tasks finish? これらのタスクが完了するまで待ちますか? @@ -1405,7 +1405,12 @@ Receiver's required difficulty: %1 and %2 GPUが正しく求められないため、OpenCLが無効になりました。 開発者に報告してください。 - + + Set notification sound... + 通知音を設定... + + + Welcome to easy and secure Bitmessage * send messages to other people @@ -1420,90 +1425,100 @@ Receiver's required difficulty: %1 and %2 - + not recommended for chans チャンネルにはお勧めしません - + Problems connecting? Try enabling UPnP in the Network Settings 接続に問題がありますか? ネットワーク設定でUPnPを有効にしてみてください - + You are trying to send an email instead of a bitmessage. This requires registering with a gateway. Attempt to register? Bitmessage の代わりにメールを送信しようとしています。 これは、ゲートウェイに登録する必要があります。 登録しますか? - + Error: Bitmessage addresses start with BM- Please check the recipient address %1 エラー: BitmessageのアドレスはBM-で始まります。 受信者のアドレス %1 を確認してください - + Error: The recipient address %1 is not typed or copied correctly. Please check it. エラー: 受信者のアドレス %1 は正しく入力、またはコピーされていません。確認して下さい。 - + Error: The recipient address %1 contains invalid characters. Please check it. エラー: 受信者のアドレス %1 は不正な文字を含んでいます。確認して下さい。 - + Error: The version of the recipient address %1 is too high. Either you need to upgrade your Bitmessage software or your acquaintance is being clever. エラー: 受信者アドレスのバージョン %1 は高すぎます。 Bitmessageソフトウェアをアップグレードする必要があるか、連絡先が賢明になっているかのいずれかです。 - + Error: Some data encoded in the recipient address %1 is too short. There might be something wrong with the software of your acquaintance. エラー: アドレス %1 でエンコードされたデータが短すぎます。連絡先のソフトウェアが何かしら誤っている可能性があります。 - + Error: Some data encoded in the recipient address %1 is too long. There might be something wrong with the software of your acquaintance. エラー: 受信者のアドレス %1 でエンコードされたデータが短すぎます。連絡先のソフトウェアが何かしら誤っている可能性があります。 - + Error: Some data encoded in the recipient address %1 is malformed. There might be something wrong with the software of your acquaintance. エラー: 受信者のアドレス %1 でエンコードされたデータの一部が不正です。連絡先のソフトウェアが何かしら誤っている可能性があります。 - + Error: Something is wrong with the recipient address %1. エラー: 受信者のアドレス %1 には何かしら誤りがあります。 - + + From %1 + 送信元 %1 + + + Synchronisation pending 同期を保留しています - + Bitmessage hasn't synchronised with the network, %n object(s) to be downloaded. If you quit now, it may cause delivery delays. Wait until the synchronisation finishes? Bitmessageはネットワークと同期していません。%n のオブジェクトをダウンロードする必要があります。 今、終了すると、配送が遅れることがあります。 同期が完了するまで待ちますか? - + Not connected 未接続 - + Bitmessage isn't connected to the network. If you quit now, it may cause delivery delays. Wait until connected and the synchronisation finishes? Bitmessageはネットワークに接続していません。 今、終了すると、配送が遅れることがあります。 接続して、同期が完了するまで待ちますか? - + Waiting for network connection... ネットワーク接続を待っています... - + Waiting for finishing synchronisation... 同期の完了を待っています... + + + You have already set a notification sound for this address book entry. Do you really want to overwrite it? + すでにこのアドレス帳エントリの通知音を設定しています。 上書きしてもよろしいですか? + MessageView From dbd12ab8b4ad970df1d6b336bbe90ead3b6cbee3 Mon Sep 17 00:00:00 2001 From: f97ada87 Date: Mon, 25 Sep 2017 19:12:00 +1000 Subject: [PATCH 230/407] fix truncation of received ackdata in objectProcessor --- src/class_objectProcessor.py | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/src/class_objectProcessor.py b/src/class_objectProcessor.py index 5d289fcc..e59645bc 100644 --- a/src/class_objectProcessor.py +++ b/src/class_objectProcessor.py @@ -97,14 +97,24 @@ class objectProcessor(threading.Thread): # Let's check whether this is a message acknowledgement bound for us. if len(data) < 32: return - if data[-32:] in shared.ackdataForWhichImWatching: + readPosition = 20 # bypass the nonce, time, and object type + # chomp version number + versionNumber, varIntLength = decodeVarint( + data[readPosition:readPosition + 10]) + readPosition += varIntLength + # chomp stream number + streamNumber, varIntLength = decodeVarint( + data[readPosition:readPosition + 10]) + readPosition += varIntLength + + if data[readPosition:] in shared.ackdataForWhichImWatching: logger.info('This object is an acknowledgement bound for me.') - del shared.ackdataForWhichImWatching[data[-32:]] + del shared.ackdataForWhichImWatching[data[readPosition:]] sqlExecute('UPDATE sent SET status=?, lastactiontime=? WHERE ackdata=?', 'ackreceived', int(time.time()), - data[-32:]) - queues.UISignalQueue.put(('updateSentItemStatusByAckdata', (data[-32:], tr._translate("MainWindow",'Acknowledgement of the message received %1').arg(l10n.formatTimestamp())))) + data[readPosition:]) + queues.UISignalQueue.put(('updateSentItemStatusByAckdata', (data[readPosition:], tr._translate("MainWindow",'Acknowledgement of the message received %1').arg(l10n.formatTimestamp())))) else: logger.debug('This object is not an acknowledgement bound for me.') From 08748fa9aeb992c4ace43e0a9fe2ea487b7b0d75 Mon Sep 17 00:00:00 2001 From: f97ada87 Date: Wed, 27 Sep 2017 00:36:02 +1000 Subject: [PATCH 231/407] move config read inside main function --- src/bitmessagemain.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/bitmessagemain.py b/src/bitmessagemain.py index 1a091c4c..06d89ba8 100755 --- a/src/bitmessagemain.py +++ b/src/bitmessagemain.py @@ -198,9 +198,11 @@ if shared.useVeryEasyProofOfWorkForTesting: defaults.networkDefaultPayloadLengthExtraBytes / 100) class Main: - def start(self, daemon=False): + def start(self): _fixSocket() + daemon = BMConfigParser().safeGetBoolean('bitmessagesettings', 'daemon') + try: opts, args = getopt.getopt(sys.argv[1:], "hcd", ["help", "curses", "daemon"]) @@ -406,8 +408,7 @@ All parameters are optional. def main(): mainprogram = Main() - mainprogram.start( - BMConfigParser().safeGetBoolean('bitmessagesettings', 'daemon')) + mainprogram.start() if __name__ == "__main__": main() From be0e724b2394c3c704147ea61f402b4757f4e806 Mon Sep 17 00:00:00 2001 From: f97ada87 Date: Sat, 30 Sep 2017 19:19:44 +1000 Subject: [PATCH 232/407] implement stealth ack objects --- src/api.py | 6 +++-- src/bitmessagecurses/__init__.py | 7 ++++-- src/bitmessageqt/__init__.py | 6 +++-- src/bitmessageqt/account.py | 4 +++- src/class_objectProcessor.py | 17 +++++--------- src/class_singleWorker.py | 19 +++++++++++---- src/class_smtpServer.py | 4 +++- src/helper_ackPayload.py | 40 ++++++++++++++++++++++++++++++++ 8 files changed, 79 insertions(+), 24 deletions(-) create mode 100644 src/helper_ackPayload.py diff --git a/src/api.py b/src/api.py index e20854fc..edb9e23d 100644 --- a/src/api.py +++ b/src/api.py @@ -35,6 +35,7 @@ import network.stats # Classes from helper_sql import sqlQuery,sqlExecute,SqlBulkExecute,sqlStoredProcedure +from helper_ackPayload import genAckPayload from debug import logger from inventory import Inventory from version import softwareVersion @@ -679,7 +680,8 @@ class MySimpleXMLRPCRequestHandler(SimpleXMLRPCRequestHandler): if not fromAddressEnabled: raise APIError(14, 'Your fromAddress is disabled. Cannot send.') - ackdata = OpenSSL.rand(32) + stealthLevel = BMConfigParser().safeGetInt('bitmessagesettings', 'ackstealthlevel') + ackdata = genAckPayload(streamNumber, stealthLevel) t = ('', toAddress, @@ -740,7 +742,7 @@ class MySimpleXMLRPCRequestHandler(SimpleXMLRPCRequestHandler): fromAddress, 'enabled') except: raise APIError(13, 'could not find your fromAddress in the keys.dat file.') - ackdata = OpenSSL.rand(32) + ackdata = genAckPayload(streamNumber, 0) toAddress = '[Broadcast subscribers]' ripe = '' diff --git a/src/bitmessagecurses/__init__.py b/src/bitmessagecurses/__init__.py index 381d7c7a..fc1d74b2 100644 --- a/src/bitmessagecurses/__init__.py +++ b/src/bitmessagecurses/__init__.py @@ -20,6 +20,7 @@ import curses import dialog from dialog import Dialog from helper_sql import * +from helper_ackPayload import genAckPayload from addresses import * import ConfigParser @@ -778,7 +779,8 @@ def sendMessage(sender="", recv="", broadcast=None, subject="", body="", reply=F if len(shared.connectedHostsList) == 0: set_background_title(d, "Not connected warning") scrollbox(d, unicode("Because you are not currently connected to the network, ")) - ackdata = OpenSSL.rand(32) + stealthLevel = BMConfigParser().safeGetInt('bitmessagesettings', 'ackstealthlevel') + ackdata = genAckPayload(streamNumber, stealthLevel) sqlExecute( "INSERT INTO sent VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)", "", @@ -802,7 +804,8 @@ def sendMessage(sender="", recv="", broadcast=None, subject="", body="", reply=F set_background_title(d, "Empty sender error") scrollbox(d, unicode("You must specify an address to send the message from.")) else: - ackdata = OpenSSL.rand(32) + # dummy ackdata, no need for stealth + ackdata = genAckPayload(streamNumber, 0) recv = BROADCAST_STR ripe = "" sqlExecute( diff --git a/src/bitmessageqt/__init__.py b/src/bitmessageqt/__init__.py index bb90bb47..09669616 100644 --- a/src/bitmessageqt/__init__.py +++ b/src/bitmessageqt/__init__.py @@ -52,6 +52,7 @@ import random import string from datetime import datetime, timedelta from helper_sql import * +from helper_ackPayload import genAckPayload import helper_search import l10n import openclpow @@ -1879,7 +1880,8 @@ class MyForm(settingsmixin.SMainWindow): if shared.statusIconColor == 'red': self.statusBar().showMessage(_translate( "MainWindow", "Warning: You are currently not connected. Bitmessage will do the work necessary to send the message but it won\'t send until you connect.")) - ackdata = OpenSSL.rand(32) + stealthLevel = BMConfigParser().safeGetInt('bitmessagesettings', 'ackstealthlevel') + ackdata = genAckPayload(streamNumber, stealthLevel) t = () sqlExecute( '''INSERT INTO sent VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)''', @@ -1933,7 +1935,7 @@ class MyForm(settingsmixin.SMainWindow): # We don't actually need the ackdata for acknowledgement since # this is a broadcast message, but we can use it to update the # user interface when the POW is done generating. - ackdata = OpenSSL.rand(32) + ackdata = genAckPayload(streamNumber, 0) toAddress = str_broadcast_subscribers ripe = '' t = ('', # msgid. We don't know what this will be until the POW is done. diff --git a/src/bitmessageqt/account.py b/src/bitmessageqt/account.py index eee6c7b4..92d497f8 100644 --- a/src/bitmessageqt/account.py +++ b/src/bitmessageqt/account.py @@ -5,6 +5,7 @@ import re import sys import inspect from helper_sql import * +from helper_ackPayload import genAckPayload from addresses import decodeAddress from bmconfigparser import BMConfigParser from foldertree import AccountMixin @@ -166,7 +167,8 @@ class GatewayAccount(BMAccount): def send(self): status, addressVersionNumber, streamNumber, ripe = decodeAddress(self.toAddress) - ackdata = OpenSSL.rand(32) + stealthLevel = BMConfigParser().safeGetInt('bitmessagesettings', 'ackstealthlevel') + ackdata = genAckPayload(streamNumber, stealthLevel) t = () sqlExecute( '''INSERT INTO sent VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)''', diff --git a/src/class_objectProcessor.py b/src/class_objectProcessor.py index e59645bc..876eff62 100644 --- a/src/class_objectProcessor.py +++ b/src/class_objectProcessor.py @@ -21,6 +21,7 @@ import helper_inbox import helper_msgcoding import helper_sent from helper_sql import * +from helper_ackPayload import genAckPayload import protocol import queues import state @@ -97,15 +98,9 @@ class objectProcessor(threading.Thread): # Let's check whether this is a message acknowledgement bound for us. if len(data) < 32: return - readPosition = 20 # bypass the nonce, time, and object type - # chomp version number - versionNumber, varIntLength = decodeVarint( - data[readPosition:readPosition + 10]) - readPosition += varIntLength - # chomp stream number - streamNumber, varIntLength = decodeVarint( - data[readPosition:readPosition + 10]) - readPosition += varIntLength + + # bypass nonce and time, retain object type/version/stream + body + readPosition = 16 if data[readPosition:] in shared.ackdataForWhichImWatching: logger.info('This object is an acknowledgement bound for me.') @@ -558,8 +553,8 @@ class objectProcessor(threading.Thread): message = time.strftime("%a, %Y-%m-%d %H:%M:%S UTC", time.gmtime( )) + ' Message ostensibly from ' + fromAddress + ':\n\n' + body fromAddress = toAddress # The fromAddress for the broadcast that we are about to send is the toAddress (my address) for the msg message we are currently processing. - ackdataForBroadcast = OpenSSL.rand( - 32) # We don't actually need the ackdataForBroadcast for acknowledgement since this is a broadcast message but we can use it to update the user interface when the POW is done generating. + # We don't actually need the ackdataForBroadcast for acknowledgement since this is a broadcast message but we can use it to update the user interface when the POW is done generating. + ackdata = genAckPayload(streamNumber, 0) toAddress = '[Broadcast subscribers]' ripe = '' diff --git a/src/class_singleWorker.py b/src/class_singleWorker.py index 58eb33c6..322bb20e 100644 --- a/src/class_singleWorker.py +++ b/src/class_singleWorker.py @@ -81,6 +81,16 @@ class singleWorker(threading.Thread, StoppableThread): logger.info('Watching for ackdata ' + hexlify(ackdata)) shared.ackdataForWhichImWatching[ackdata] = 0 + # Fix legacy (headerless) watched ackdata to include header + for oldack in shared.ackdataForWhichImWatching.keys(): + if (len(oldack)==32): + # attach legacy header, always constant (msg/1/1) + newack = '\x00\x00\x00\x02\x01\x01' + oldack + shared.ackdataForWhichImWatching[newack] = 0 + sqlExecute('UPDATE sent SET ackdata=? WHERE ackdata=?', + newack, oldack ) + del shared.ackdataForWhichImWatching[oldack] + self.stop.wait( 10) # give some time for the GUI to start before we start on existing POW tasks. @@ -967,11 +977,10 @@ class singleWorker(threading.Thread, StoppableThread): TTL = 28*24*60*60 # 4 weeks TTL = int(TTL + random.randrange(-300, 300)) # Add some randomness to the TTL embeddedTime = int(time.time() + TTL) - payload = pack('>Q', (embeddedTime)) - payload += '\x00\x00\x00\x02' # object type: msg - payload += encodeVarint(1) # msg version - payload += encodeVarint(toStreamNumber) + ackdata - + + # type/version/stream already included + payload = pack('>Q', (embeddedTime)) + ackdata + target = 2 ** 64 / (defaults.networkDefaultProofOfWorkNonceTrialsPerByte*(len(payload) + 8 + defaults.networkDefaultPayloadLengthExtraBytes + ((TTL*(len(payload)+8+defaults.networkDefaultPayloadLengthExtraBytes))/(2 ** 16)))) logger.info('(For ack message) Doing proof of work. TTL set to ' + str(TTL)) diff --git a/src/class_smtpServer.py b/src/class_smtpServer.py index 3bc81a61..b62a7130 100644 --- a/src/class_smtpServer.py +++ b/src/class_smtpServer.py @@ -14,6 +14,7 @@ from addresses import decodeAddress from bmconfigparser import BMConfigParser from debug import logger from helper_sql import sqlExecute +from helper_ackPayload import genAckPayload from helper_threading import StoppableThread from pyelliptic.openssl import OpenSSL import queues @@ -65,7 +66,8 @@ class smtpServerPyBitmessage(smtpd.SMTPServer): def send(self, fromAddress, toAddress, subject, message): status, addressVersionNumber, streamNumber, ripe = decodeAddress(toAddress) - ackdata = OpenSSL.rand(32) + stealthLevel = BMConfigParser().safeGetInt('bitmessagesettings', 'ackstealthlevel') + ackdata = genAckPayload(streamNumber, stealthLevel) t = () sqlExecute( '''INSERT INTO sent VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)''', diff --git a/src/helper_ackPayload.py b/src/helper_ackPayload.py new file mode 100644 index 00000000..ef99ec2a --- /dev/null +++ b/src/helper_ackPayload.py @@ -0,0 +1,40 @@ +import hashlib +import highlevelcrypto +import random +import helper_random +from binascii import hexlify, unhexlify +from struct import pack, unpack +from addresses import encodeVarint + +# This function generates payload objects for message acknowledgements +# Several stealth levels are available depending on the privacy needs; +# a higher level means better stealth, but also higher cost (size+POW) +# - level 0: a random 32-byte sequence with a message header appended +# - level 1: a getpubkey request for a (random) dummy key hash +# - level 2: a standard message, encrypted to a random pubkey + +def genAckPayload(streamNumber=1, stealthLevel=0): + if (stealthLevel==2): # Generate privacy-enhanced payload + # Generate a dummy privkey and derive the pubkey + dummyPubKeyHex = highlevelcrypto.privToPub(hexlify(helper_random.randomBytes(32))) + # Generate a dummy message of random length + # (the smallest possible standard-formatted message is 234 bytes) + dummyMessage = helper_random.randomBytes(random.randint(234, 800)) + # Encrypt the message using standard BM encryption (ECIES) + ackdata = highlevelcrypto.encrypt(dummyMessage, dummyPubKeyHex) + acktype = 2 # message + version = 1 + + elif (stealthLevel==1): # Basic privacy payload (random getpubkey) + ackdata = helper_random.randomBytes(32) + acktype = 0 # getpubkey + version = 4 + + else: # Minimum viable payload (non stealth) + ackdata = helper_random.randomBytes(32) + acktype = 2 # message + version = 1 + + ackobject = pack('>I', acktype) + encodeVarint(version) + encodeVarint(streamNumber) + ackdata + + return ackobject From b1442ecb0a1e8c383040a071c648624c38fef5c5 Mon Sep 17 00:00:00 2001 From: Peter Surda Date: Sat, 30 Sep 2017 13:42:04 +0200 Subject: [PATCH 233/407] Dandelion fixes and updates - also, randomise the item order in an inv/dinv command --- src/network/bmproto.py | 6 ++---- src/network/dandelion.py | 10 ++++++---- src/network/invthread.py | 26 +++++++++++++++----------- 3 files changed, 23 insertions(+), 19 deletions(-) diff --git a/src/network/bmproto.py b/src/network/bmproto.py index 66f066ef..8099c5fc 100644 --- a/src/network/bmproto.py +++ b/src/network/bmproto.py @@ -274,7 +274,7 @@ class BMProto(AdvancedDispatcher, ObjectTracker): random.shuffle(items) for i in items: if i in DandelionStems().stem and \ - self not in DandelionStems().stem[i]: + self != DandelionStems().stem[i]: self.antiIntersectionDelay() logger.info('%s asked for a stem object we didn\'t offer to it.', self.destination) break @@ -324,9 +324,7 @@ class BMProto(AdvancedDispatcher, ObjectTracker): self.dandelionRefresh = time.time() + REASSIGN_INTERVAL for i in items: - # Fluff trigger by RNG, per item - if random.randint(1, 100) < BMConfigParser().safeGetBoolean("network", "dandelion"): - DandelionStem().add(i, self.dandelionRoutes) + DandelionStems().add(i, self, self.dandelionRoutes) self.handleReceivedInventory(i) return True diff --git a/src/network/dandelion.py b/src/network/dandelion.py index ea27915f..045f7288 100644 --- a/src/network/dandelion.py +++ b/src/network/dandelion.py @@ -1,7 +1,6 @@ -import random +from random import choice from threading import RLock -import protocol from singleton import Singleton # randomise routes after 600 seconds @@ -12,18 +11,21 @@ FLUFF_TRIGGER_TIMEOUT = 300 class DandelionStems(): def __init__(self): self.stem = {} + self.source = {} self.timeouts = {} self.lock = RLock() - def add(self, hashId, stems): + def add(self, hashId, source, stems): with self.lock: - self.stem[hashId] = stems + self.stem[hashId] = choice(stems) + self.source[hashId] = source self.timeouts[hashId] = time.time() def remove(self, hashId): with self.lock: try: del self.stem[hashId] + del self.source[hashId] del self.timeouts[hashId] except KeyError: pass diff --git a/src/network/invthread.py b/src/network/invthread.py index 992930db..574e8a3a 100644 --- a/src/network/invthread.py +++ b/src/network/invthread.py @@ -1,5 +1,5 @@ import Queue -from random import randint +from random import randint, shuffle import threading from time import time @@ -35,11 +35,8 @@ class InvThread(threading.Thread, StoppableThread): data = invQueue.get(False) # locally generated if len(data) == 2: + DandelionStems.add(data[1], None, self.dandelionRoutes) BMConnectionPool().handleReceivedObject(data[0], data[1]) - # Fluff trigger by RNG - # auto-ignore if config set to 0, i.e. dandelion is off - if randint(1, 100) < BMConfigParser().safeGetBoolean("network", "dandelion"): - DandelionStems.add(data[1], self.dandelionRoutes) # came over the network else: source = BMConnectionPool().getConnectionByAddr(data[2]) @@ -59,21 +56,28 @@ class InvThread(threading.Thread, StoppableThread): for inv in chunk: if inv[0] not in connection.streams: continue - if inv[1] in DandelionStems().stem: - if connection in DandelionStems().stem[inv[1]]: - stems.append(inv[1]) - continue - # else try: with connection.objectsNewToThemLock: del connection.objectsNewToThem[inv[1]] - fluffs.append(inv[1]) except KeyError: continue + if inv[1] in DandelionStems().stem: + if connection == DandelionStems().stem[inv[1]]: + # Fluff trigger by RNG + # auto-ignore if config set to 0, i.e. dandelion is off + if randint(1, 100) < BMConfigParser().safeGetBoolean("network", "dandelion"): + stems.append(inv[1]) + else: + fluffs.append(inv[1]) + continue + else: + fluffs.append(inv[1]) if fluffs: + shuffle(fluffs) connection.append_write_buf(protocol.CreatePacket('inv', \ addresses.encodeVarint(len(fluffs)) + "".join(fluffs))) if stems: + shuffle(stems) connection.append_write_buf(protocol.CreatePacket('dinv', \ addresses.encodeVarint(len(stems)) + "".join(stems))) invQueue.iterate() From 6548999a49a1943fc4c17cd51584e831b1610db7 Mon Sep 17 00:00:00 2001 From: Peter Surda Date: Mon, 2 Oct 2017 08:02:29 +0200 Subject: [PATCH 234/407] Dandelion fix - thanks to g1itch for reporting - addresses #1049 --- src/network/invthread.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/network/invthread.py b/src/network/invthread.py index 574e8a3a..cbed7a70 100644 --- a/src/network/invthread.py +++ b/src/network/invthread.py @@ -35,7 +35,7 @@ class InvThread(threading.Thread, StoppableThread): data = invQueue.get(False) # locally generated if len(data) == 2: - DandelionStems.add(data[1], None, self.dandelionRoutes) + DandelionStems().add(data[1], None, self.dandelionRoutes) BMConnectionPool().handleReceivedObject(data[0], data[1]) # came over the network else: From 1abdc148077058f25f090aef95ad8a3ffdfe6ec8 Mon Sep 17 00:00:00 2001 From: Dmitri Bogomolov <4glitch@gmail.com> Date: Tue, 3 Oct 2017 18:01:54 +0300 Subject: [PATCH 235/407] Fix statusbar chan creation message: non-ASCII characters displayed incorrectly --- src/bitmessageqt/newchandialog.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/bitmessageqt/newchandialog.py b/src/bitmessageqt/newchandialog.py index a7784206..b28d2bec 100644 --- a/src/bitmessageqt/newchandialog.py +++ b/src/bitmessageqt/newchandialog.py @@ -37,7 +37,7 @@ class NewChanDialog(QtGui.QDialog, RetranslateMixin): addressGeneratorQueue.put(('joinChan', addBMIfNotPresent(self.chanAddress.text().toUtf8()), str_chan + ' ' + str(self.chanPassPhrase.text().toUtf8()), self.chanPassPhrase.text().toUtf8(), True)) addressGeneratorReturnValue = apiAddressGeneratorReturnQueue.get(True) if len(addressGeneratorReturnValue) > 0 and addressGeneratorReturnValue[0] != 'chan name does not match address': - UISignalQueue.put(('updateStatusBar', _translate("newchandialog", "Successfully created / joined chan %1").arg(str(self.chanPassPhrase.text().toUtf8())))) + UISignalQueue.put(('updateStatusBar', _translate("newchandialog", "Successfully created / joined chan %1").arg(unicode(self.chanPassPhrase.text())))) self.parent.ui.tabWidget.setCurrentIndex(3) self.done(QtGui.QDialog.Accepted) else: From 333170b17256842a12a00bebe0e3e0863271d02a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Peter=20=C5=A0urda?= Date: Fri, 6 Oct 2017 12:19:34 +0200 Subject: [PATCH 236/407] Dandelion fixes - more exception handling - only use outbound connections for stems (thanks to @amillter for info) - don't create stems if config disabled - addresses #1049 --- src/network/connectionpool.py | 3 +-- src/network/dandelion.py | 11 +++++++++-- 2 files changed, 10 insertions(+), 4 deletions(-) diff --git a/src/network/connectionpool.py b/src/network/connectionpool.py index 2943200b..7ae8afd9 100644 --- a/src/network/connectionpool.py +++ b/src/network/connectionpool.py @@ -55,8 +55,7 @@ class BMConnectionPool(object): # Choose 2 peers randomly # TODO: handle streams peers = [] - connections = self.inboundConnections.values() + \ - self.outboundConnections.values() + connections = self.outboundConnections.values() random.shuffle(connections) for i in connections: if i == node: diff --git a/src/network/dandelion.py b/src/network/dandelion.py index 045f7288..840cc909 100644 --- a/src/network/dandelion.py +++ b/src/network/dandelion.py @@ -1,6 +1,8 @@ from random import choice from threading import RLock +from time import time +from bmconfigparser import BMConfigParser from singleton import Singleton # randomise routes after 600 seconds @@ -16,10 +18,15 @@ class DandelionStems(): self.lock = RLock() def add(self, hashId, source, stems): + if BMConfigParser().safeGetInt('network', 'dandelion') == 0: + return with self.lock: - self.stem[hashId] = choice(stems) + try: + self.stem[hashId] = choice(stems) + except IndexError: + self.stem = None self.source[hashId] = source - self.timeouts[hashId] = time.time() + self.timeouts[hashId] = time() def remove(self, hashId): with self.lock: From a49b3b5f848783f6df9a4483a41b9e50c50a89ce Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Peter=20=C5=A0urda?= Date: Fri, 6 Oct 2017 18:26:06 +0200 Subject: [PATCH 237/407] Asyncore UDP fix - when there was an error writing to a udp socket, it wasn't handled correctly --- src/network/udp.py | 1 + 1 file changed, 1 insertion(+) diff --git a/src/network/udp.py b/src/network/udp.py index 27cf0abb..bd867125 100644 --- a/src/network/udp.py +++ b/src/network/udp.py @@ -151,6 +151,7 @@ class UDPSocket(BMProto): retval = self.socket.sendto(self.write_buf, ('', UDPSocket.port)) except socket.error as e: logger.error("socket error on sendato: %s", str(e)) + retval = 0 self.slice_write_buf(retval) From 1eb0dd6f01f75184ea3145b6f7bda852fc0f1fd7 Mon Sep 17 00:00:00 2001 From: PyBitmessage Translations Date: Sun, 15 Oct 2017 13:45:31 +0200 Subject: [PATCH 238/407] Auto-updated language fr from transifex --- src/translations/bitmessage_fr.qm | Bin 97042 -> 98998 bytes src/translations/bitmessage_fr.ts | 461 +++++++++++++++++------------- 2 files changed, 263 insertions(+), 198 deletions(-) diff --git a/src/translations/bitmessage_fr.qm b/src/translations/bitmessage_fr.qm index 2eccb916105d2b8c6b91deea59a3f929ba821f02..742e62d828d8f0097737237cc4353a8ca0d454d8 100644 GIT binary patch delta 4700 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 delta 3364 zcmX|D3s{V48@}K9zHer}nQvyuAvvU$a_Art9ad2yhq6eCB+_o8l2{crCC4^GC8Z+D zV#!ugWaZFW8=xkt3y>%RbBEIJr-6|1 zU||m6ubKhjW5GAi10r(3KeGWM4Lgx(H4svGApJOmUGIUziy-K8#sk?t5OTbMJsl8^ z6o3WTK&aRZ78D5iA&xcq!&4|An6aB3|qdei$q#=)|tADDM8hF_%boA*I)yFCx69)Zy}lYn<;;biy% z2n~gM!8%~I8z%0o1_leA$arH+`g11WTn5jKmsH^ec&N5{}}^-`2wr~1af zM1w8`On8mv<&D6O=eW~{(y?j6eYaCY^ly0kGnHHXgo%6K(EF~;V2>88cLD3)n+Npq zCz-rg|<+IC@9gweM?+Pj4G9%U@a|4EW%7oJt_|O}f6!sWs9VxSLxl07L z$*j&&Ki4_QtXc_^{*l?NS_@osmJNTm7OZEI%&9dISaV4>F@Z2{mn>v#8xeI{w$!Rj zPl1-o!Zw8iU(S{3<060&)w1N9UBRr1WGPl<^!%l4%d!{1rRTCefe!$SF0upu^nO@^ ztW--0b_lZCK*F7CWH-YGl60@in&K{yBBsh(v-ud4>-#0In@>`oIiK%&*bhwZ%#SH2*O-2vck?2OUsdxSwIsr@ zfqd{WYZ~@+UcW?L1jc9bO9#gS@)Ca4zr^)_y!pho8^HOa{H9UF>4q!(jvS(DbUpt= zDg)eC@<)bHqGulPM=T5J`vJZ*r#n#9z@Iwo3})TLpL;e0Y=$*o9b*PeYv*qooTulJ zd`s19(o_zAca<@4{A<0yZd(H`(*>&x!b2Xy=$E^I1Utc@<|SBa6m!XjVdHlw@nwcR<=&}AVak6P{CEF{?0fsM@*k`jW!=1dT_ zIMV(H(}i6QLx4Y~2-&Zrz((mKgx#AcV794Hv{@kAc_B%jAK}^T4#}^02SN$+(}& zBW~CdrAqnAZ8UasR?9biOVkCm$a5w?1k*2Rmlyn24{UlOKbYnRm?g-MT2Q6)YUQO{ zy8&-Yrnx_ROy2-B}HlXoYAiv{79QqW>U&tv?NvXnM8&R~;KrvcHmG>=H zI4&3jq`gykHrD~W-Y9&w#gYHoD}skFCQWo#B<*oQA9##bY?x{U=5MFiYIhfym7>_z zlhohyU0R#mTrZqZ{E$M!fsoH=vHLT&-|9prBq_?L z0R!f%LR%@**9SY1*=<$D`_l4_@)F$KJxs48-PPcHRPRqFeZOu<5RQvHT1o1{8h z=LlwOpt_JiGk0)?>gom;VC4+eAN~)4mIbQknUvV%)oSKxRzwont`=j0feF8=Ra^s* z8mU%2SOI2fsMaL9Q|0w(xuUVYS*LcMeFg|lEct z)>l2f;}F=am+GL^Bn0CE^>?vJgiF=is#++&7p04U=27{=B9_nW%;Mhbo=rMr*<7Bx!g3nvp(FB_#p(XZ)pU@Dau&_)CIOC@87 zxvG9r$tP)7e3U98NTYwxlPXyhxnM#kvN>0zs-5G2ZKtHV$MJNNY>=*odI2Atr3T## zI-0ji&24l!n|V*Vy}FSC8cJ_A5+PX!HOfb2#8`=@&r&ne&eLm}fk77l8=1yDivq;{ zps`9j4E+1M#wNLvG;EX>thUxq8mC$Emf|!PXu^+&(@a0nMCg5i_z+F3TPhJ9rdhMb znP#m)lXt5Q_`R#dbS{Udf?Z(%Nh5h8fW;;WG|3{*3KQ zI*|!qXq&z0PV?qTC$do=w0}(>s3%F7YCjrl!G>+t4KwRaZsDP`4PQyuNei9bC1bj+ z#_F8YP9<#7O)GE)t`+L${CEbeOR+96dmXuAh%P+Uf!zPHF4B@JH@c%s zoIvYMzSX6+6a&d6x&q^7BBH16aP3Dh$K|@BiH;N)S9KNr^T>`)=qgvd04_M`&i5cJ zH`HAV_X57M)IA(S!?=6B?){c^bd6M#_lLD-{?mi`l>VrRjOZ!Hcm%^l`Kfw2{R EmailGatewayRegistrationDialog - + Registration failed: L’inscription a échoué : - + The requested email address is not available, please try a new one. Fill out the new desired email address (including @mailchuck.com) below: L’adresse de courriel demandée n’est pas disponible, veuillez en essayer une nouvelle. Saisissez ci-dessous la nouvelle adresse de courriel désirée (incluant @mailchuck.com) : @@ -173,52 +173,52 @@ Veuillez taper l’adresse de courriel désirée (incluant @mailchuck.com) : MainWindow - + Reply to sender Répondre à l’expéditeur - + Reply to channel Répondre au canal - + Add sender to your Address Book Ajouter l’expéditeur au carnet d’adresses - + Add sender to your Blacklist Ajouter l’expéditeur à votre liste noire - + Move to Trash Envoyer à la Corbeille - + Undelete Restaurer - + View HTML code as formatted text Voir le code HTML comme du texte formaté - + Save message as... Enregistrer le message sous… - + Mark Unread Marquer comme non-lu - + New Nouvelle @@ -243,12 +243,12 @@ Veuillez taper l’adresse de courriel désirée (incluant @mailchuck.com) :Copier l’adresse dans le presse-papier - + Special address behavior... Comportement spécial de l’adresse… - + Email gateway Passerelle de courriel @@ -258,37 +258,37 @@ Veuillez taper l’adresse de courriel désirée (incluant @mailchuck.com) :Effacer - + Send message to this address Envoyer un message à cette adresse - + Subscribe to this address S’abonner à cette adresse - + Add New Address Ajouter une nouvelle adresse - + Copy destination address to clipboard Copier l’adresse de destination dans le presse-papier - + Force send Forcer l’envoi - + One of your addresses, %1, is an old version 1 address. Version 1 addresses are no longer supported. May we delete it now? Une de vos adresses, %1, est une vieille adresse de la version 1. Les adresses de la version 1 ne sont plus supportées. Nous pourrions la supprimer maintenant? - + Waiting for their encryption key. Will request it again soon. En attente de la clé de chiffrement. Une nouvelle requête sera bientôt lancée. @@ -298,17 +298,17 @@ Veuillez taper l’adresse de courriel désirée (incluant @mailchuck.com) : - + Queued. En attente. - + Message sent. Waiting for acknowledgement. Sent at %1 Message envoyé. En attente de l’accusé de réception. Envoyé %1 - + Message sent. Sent at %1 Message envoyé. Envoyé %1 @@ -318,47 +318,47 @@ Veuillez taper l’adresse de courriel désirée (incluant @mailchuck.com) : - + Acknowledgement of the message received %1 Accusé de réception reçu %1 - + Broadcast queued. Message de diffusion en attente. - + Broadcast on %1 Message de diffusion du %1 - + Problem: The work demanded by the recipient is more difficult than you are willing to do. %1 Problème : Le travail demandé par le destinataire est plus difficile que ce que vous avez paramétré. %1 - + Problem: The recipient's encryption key is no good. Could not encrypt message. %1 Problème : la clé de chiffrement du destinataire n’est pas bonne. Il n’a pas été possible de chiffrer le message. %1 - + Forced difficulty override. Send should start soon. Neutralisation forcée de la difficulté. L’envoi devrait bientôt commencer. - + Unknown status: %1 %2 Statut inconnu : %1 %2 - + Not Connected Déconnecté - + Show Bitmessage Afficher Bitmessage @@ -368,12 +368,12 @@ Veuillez taper l’adresse de courriel désirée (incluant @mailchuck.com) :Envoyer - + Subscribe S’abonner - + Channel Canal @@ -383,12 +383,12 @@ Veuillez taper l’adresse de courriel désirée (incluant @mailchuck.com) :Quitter - + You may manage your keys by editing the keys.dat file stored in the same directory as this program. It is important that you back up this file. Vous pouvez éditer vos clés en éditant le fichier keys.dat stocké dans le même répertoire que ce programme. Il est important de faire des sauvegardes de ce fichier. - + You may manage your keys by editing the keys.dat file stored in %1 It is important that you back up this file. @@ -396,54 +396,54 @@ It is important that you back up this file. Il est important de faire des sauvegardes de ce fichier. - + Open keys.dat? Ouvrir keys.dat ? - + You may manage your keys by editing the keys.dat file stored in the same directory as this program. It is important that you back up this file. Would you like to open the file now? (Be sure to close Bitmessage before making any changes.) Vous pouvez éditer vos clés en éditant le fichier keys.dat stocké dans le même répertoire que ce programme. Il est important de faire des sauvegardes de ce fichier. Souhaitez-vous l’ouvrir maintenant ? (Assurez-vous de fermer Bitmessage avant d’effectuer des changements.) - + You may manage your keys by editing the keys.dat file stored in %1 It is important that you back up this file. Would you like to open the file now? (Be sure to close Bitmessage before making any changes.) Vous pouvez éditer vos clés en éditant le fichier keys.dat stocké dans le répertoire %1. Il est important de faire des sauvegardes de ce fichier. Souhaitez-vous l’ouvrir maintenant? (Assurez-vous de fermer Bitmessage avant d’effectuer des changements.) - + Delete trash? Supprimer la corbeille ? - + Are you sure you want to delete all trashed messages? Êtes-vous sûr de vouloir supprimer tous les messages dans la corbeille ? - + bad passphrase Mauvaise phrase secrète - + You must type your passphrase. If you don't have one then this is not the form for you. Vous devez taper votre phrase secrète. Si vous n’en avez pas, ce formulaire n’est pas pour vous. - + Bad address version number Mauvais numéro de version d’adresse - + Your address version number must be a number: either 3 or 4. Votre numéro de version d’adresse doit être un nombre : soit 3 soit 4. - + Your address version number must be either 3 or 4. Votre numéro de version d’adresse doit être soit 3 soit 4. @@ -513,22 +513,22 @@ It is important that you back up this file. Would you like to open the file now? - + Connection lost Connexion perdue - + Connected Connecté - + Message trashed Message envoyé à la corbeille - + The TTL, or Time-To-Live is the length of time that the network will hold the message. The recipient must get it during this time. If your Bitmessage client does not hear an acknowledgement, it will resend the message automatically. The longer the Time-To-Live, the @@ -537,17 +537,17 @@ It is important that you back up this file. Would you like to open the file now? Le destinataire doit l’obtenir avant ce temps. Si votre client Bitmessage ne reçoit pas de confirmation de réception, il va le ré-envoyer automatiquement. Plus le Time-To-Live est long, plus grand est le travail que votre ordinateur doit effectuer pour envoyer le message. Un Time-To-Live de quatre ou cinq jours est souvent approprié. - + Message too long Message trop long - + The message that you are trying to send is too long by %1 bytes. (The maximum is 261644 bytes). Please cut it down before sending. Le message que vous essayez d’envoyer est trop long de %1 octets (le maximum est 261644 octets). Veuillez le réduire avant de l’envoyer. - + Error: Your account wasn't registered at an email gateway. Sending registration now as %1, please wait for the registration to be processed before retrying sending. Erreur : votre compte n’a pas été inscrit à une passerelle de courrier électronique. Envoi de l’inscription maintenant en tant que %1, veuillez patienter tandis que l’inscription est en cours de traitement, avant de retenter l’envoi. @@ -592,67 +592,67 @@ Le destinataire doit l’obtenir avant ce temps. Si votre client Bitmessage ne r - + Error: You must specify a From address. If you don't have one, go to the 'Your Identities' tab. Erreur : Vous devez spécifier une adresse d’expéditeur. Si vous n’en avez pas, rendez-vous dans l’onglet 'Vos identités'. - + Address version number Numéro de version de l’adresse - + Concerning the address %1, Bitmessage cannot understand address version numbers of %2. Perhaps upgrade Bitmessage to the latest version. Concernant l’adresse %1, Bitmessage ne peut pas comprendre les numéros de version de %2. Essayez de mettre à jour Bitmessage vers la dernière version. - + Stream number Numéro de flux - + Concerning the address %1, Bitmessage cannot handle stream numbers of %2. Perhaps upgrade Bitmessage to the latest version. Concernant l’adresse %1, Bitmessage ne peut pas supporter les nombres de flux de %2. Essayez de mettre à jour Bitmessage vers la dernière version. - + Warning: You are currently not connected. Bitmessage will do the work necessary to send the message but it won't send until you connect. Avertissement : Vous êtes actuellement déconnecté. Bitmessage fera le travail nécessaire pour envoyer le message mais il ne sera pas envoyé tant que vous ne vous connecterez pas. - + Message queued. Message mis en file d’attente. - + Your 'To' field is empty. Votre champ 'Vers' est vide. - + Right click one or more entries in your address book and select 'Send message to this address'. Cliquez droit sur une ou plusieurs entrées dans votre carnet d’adresses et sélectionnez 'Envoyer un message à ces adresses'. - + Fetched address from namecoin identity. Récupération avec succès de l’adresse de l’identité Namecoin. - + New Message Nouveau message - + From - De + - + Sending email gateway registration request Envoi de la demande d’inscription de la passerelle de courriel @@ -667,142 +667,142 @@ Le destinataire doit l’obtenir avant ce temps. Si votre client Bitmessage ne r L’adresse que vous avez entrée est invalide. Adresse ignorée. - + Error: You cannot add the same address to your address book twice. Try renaming the existing one if you want. Erreur : Vous ne pouvez pas ajouter une adresse déjà présente dans votre carnet d’adresses. Essayez de renommer l’adresse existante. - + Error: You cannot add the same address to your subscriptions twice. Perhaps rename the existing one if you want. Erreur : vous ne pouvez pas ajouter la même adresse deux fois à vos abonnements. Peut-être que vous pouvez renommer celle qui existe si vous le souhaitez. - + Restart Redémarrer - + You must restart Bitmessage for the port number change to take effect. Vous devez redémarrer Bitmessage pour que le changement de port prenne effet. - + Bitmessage will use your proxy from now on but you may want to manually restart Bitmessage now to close existing connections (if any). Bitmessage utilisera votre proxy dorénavant, mais vous pouvez redémarrer manuellement Bitmessage maintenant afin de fermer des connexions existantes (si il y en existe). - + Number needed Nombre requis - + Your maximum download and upload rate must be numbers. Ignoring what you typed. Vos taux maximum de téléchargement et de téléversement doivent être des nombres. Ce que vous avez tapé est ignoré. - + Will not resend ever Ne renverra jamais - + Note that the time limit you entered is less than the amount of time Bitmessage waits for the first resend attempt therefore your messages will never be resent. Notez que la limite de temps que vous avez entrée est plus courte que le temps d’attente respecté par Bitmessage avant le premier essai de renvoi, par conséquent votre message ne sera jamais renvoyé. - + Sending email gateway unregistration request Envoi de la demande de désinscription de la passerelle de courriel - + Sending email gateway status request Envoi à la passerelle de courriel d’une demande de statut - + Passphrase mismatch Phrases secrètes différentes - + The passphrase you entered twice doesn't match. Try again. Les phrases secrètes entrées sont différentes. Réessayez. - + Choose a passphrase Choisissez une phrase secrète - + You really do need a passphrase. Vous devez vraiment utiliser une phrase secrète. - + Address is gone L’adresse a disparu - + Bitmessage cannot find your address %1. Perhaps you removed it? Bitmessage ne peut pas trouver votre adresse %1. Peut-être l’avez-vous supprimée? - + Address disabled Adresse désactivée - + Error: The address from which you are trying to send is disabled. You'll have to enable it on the 'Your Identities' tab before using it. Erreur : L’adresse avec laquelle vous essayez de communiquer est désactivée. Vous devez d’abord l’activer dans l’onglet 'Vos identités' avant de l’utiliser. - + Entry added to the Address Book. Edit the label to your liking. Entrée ajoutée au carnet d’adresse. Éditez l’étiquette à votre convenance. - + Entry added to the blacklist. Edit the label to your liking. Entrée ajoutée à la liste noire. Éditez l’étiquette à votre convenance. - + Error: You cannot add the same address to your blacklist twice. Try renaming the existing one if you want. Erreur : vous ne pouvez pas ajouter la même adresse deux fois à votre liste noire. Essayez de renommer celle qui existe si vous le souhaitez. - + Moved items to trash. Messages déplacés dans la corbeille. - + Undeleted item. Articles restaurés. - + Save As... Enregistrer sous… - + Write error. Erreur d’écriture. - + No addresses selected. Aucune adresse sélectionnée. - + If you delete the subscription, messages that you already received will become inaccessible. Maybe you can consider disabling the subscription instead. Disabled subscriptions will not receive new messages, but you can still view messages you already received. Are you sure you want to delete the subscription? @@ -811,7 +811,7 @@ Are you sure you want to delete the subscription? Êtes-vous sur de vouloir supprimer cet abonnement ? - + If you delete the channel, messages that you already received will become inaccessible. Maybe you can consider disabling the channel instead. Disabled channels will not receive new messages, but you can still view messages you already received. Are you sure you want to delete the channel? @@ -820,92 +820,92 @@ Are you sure you want to delete the channel? Êtes-vous sûr de vouloir supprimer ce canal ? - + Do you really want to remove this avatar? Voulez-vous vraiment enlever cet avatar ? - + You have already set an avatar for this address. Do you really want to overwrite it? Vous avez déjà mis un avatar pour cette adresse. Voulez-vous vraiment l’écraser ? - + Start-on-login not yet supported on your OS. Le démarrage dès l’ouverture de session n’est pas encore supporté sur votre OS. - + Minimize-to-tray not yet supported on your OS. La minimisation en zone système n’est pas encore supportée sur votre OS. - + Tray notifications not yet supported on your OS. Les notifications en zone système ne sont pas encore supportées sur votre OS. - + Testing... Tester… - + This is a chan address. You cannot use it as a pseudo-mailing list. Ceci est une adresse de canal. Vous ne pouvez pas l’utiliser en tant que pseudo liste de diffusion. - + The address should start with ''BM-'' L’adresse devrait commencer avec "BM-" - + The address is not typed or copied correctly (the checksum failed). L’adresse n’est pas correcte (la somme de contrôle a échoué). - + The version number of this address is higher than this software can support. Please upgrade Bitmessage. Le numéro de version de cette adresse est supérieur à celui que le programme peut supporter. Veuiller mettre Bitmessage à jour. - + The address contains invalid characters. L’adresse contient des caractères invalides. - + Some data encoded in the address is too short. Certaines données encodées dans l’adresse sont trop courtes. - + Some data encoded in the address is too long. Certaines données encodées dans l’adresse sont trop longues. - + Some data encoded in the address is malformed. Quelques données codées dans l’adresse sont mal formées. - + Enter an address above. Entrez ci-dessus une adresse. - + Address is an old type. We cannot display its past broadcasts. L’adresse est d’ancien type. Nous ne pouvons pas montrer ses messages de diffusion passés. - + There are no recent broadcasts from this address to display. Il n’y a aucun message de diffusion récent de cette adresse à afficher. - + You are using TCP port %1. (This can be changed in the settings). Vous utilisez le port TCP %1. (Ceci peut être changé dans les paramètres). @@ -1115,47 +1115,47 @@ Are you sure you want to delete the channel? Ajouter une nouvelle entrée - + Display the %1 recent broadcast(s) from this address. Montre le(s) %1 plus récent(s) message(s) de diffusion issu(s) de cette adresse. - + New version of PyBitmessage is available: %1. Download it from https://github.com/Bitmessage/PyBitmessage/releases/latest Une nouvelle version de PyBitmessage est disponible : %1. Veuillez la télécharger depuis https://github.com/Bitmessage/PyBitmessage/releases/latest - + Waiting for PoW to finish... %1% En attente de la fin de la PoW… %1% - + Shutting down Pybitmessage... %1% Pybitmessage en cours d’arrêt… %1% - + Waiting for objects to be sent... %1% En attente de l’envoi des objets… %1% - + Saving settings... %1% Enregistrement des paramètres… %1% - + Shutting down core... %1% Cœur en cours d’arrêt… %1% - + Stopping notifications... %1% Arrêt des notifications… %1% - + Shutdown imminent... %1% Arrêt imminent… %1% @@ -1165,17 +1165,17 @@ Are you sure you want to delete the channel? %n heure%n heures - + %n day(s) %n jour%n jours - + Shutting down PyBitmessage... %1% PyBitmessage en cours d’arrêt… %1% - + Sent Envoyé @@ -1220,146 +1220,146 @@ Are you sure you want to delete the channel? Alerte : votre disque ou le volume de stockage de données est plein. Bitmessage va maintenant se fermer. - + Error! Could not find sender address (your address) in the keys.dat file. Erreur ! Il n’a pas été possible de trouver l’adresse d’expéditeur (votre adresse) dans le fichier keys.dat. - + Doing work necessary to send broadcast... Travail en cours afin d’envoyer le message de diffusion… - + Broadcast sent on %1 Message de diffusion envoyé %1 - + Encryption key was requested earlier. La clé de chiffrement a été demandée plus tôt. - + Sending a request for the recipient's encryption key. Envoi d’une demande de la clé de chiffrement du destinataire. - + Looking up the receiver's public key Recherche de la clé publique du récepteur - + Problem: Destination is a mobile device who requests that the destination be included in the message but this is disallowed in your settings. %1 Problème : la destination est un dispositif mobile qui nécessite que la destination soit incluse dans le message mais ceci n’est pas autorisé dans vos paramètres. %1 - + Doing work necessary to send message. There is no required difficulty for version 2 addresses like this. Travail en cours afin d’envoyer le message. Il n’y a pas de difficulté requise pour les adresses version 2 comme celle-ci. - + Doing work necessary to send message. Receiver's required difficulty: %1 and %2 Travail en cours afin d’envoyer le message. Difficulté requise du destinataire : %1 et %2 - + Problem: The work demanded by the recipient (%1 and %2) is more difficult than you are willing to do. %3 Problème : Le travail demandé par le destinataire (%1 and %2) est plus difficile que ce que vous avez paramétré. %3 - + Problem: You are trying to send a message to yourself or a chan but your encryption key could not be found in the keys.dat file. Could not encrypt message. %1 Problème : Vous essayez d’envoyer un message à un canal ou à vous-même mais votre clef de chiffrement n’a pas été trouvée dans le fichier keys.dat. Le message ne peut pas être chiffré. %1 - + Doing work necessary to send message. Travail en cours afin d’envoyer le message. - + Message sent. Waiting for acknowledgement. Sent on %1 Message envoyé. En attente de l’accusé de réception. Envoyé %1 - + Doing work necessary to request encryption key. Travail en cours afin d’obtenir la clé de chiffrement. - + Broadcasting the public key request. This program will auto-retry if they are offline. Diffusion de la demande de clef publique. Ce programme réessaiera automatiquement si ils sont déconnectés. - + Sending public key request. Waiting for reply. Requested at %1 Envoi d’une demande de clef publique. En attente d’une réponse. Demandée à %1 - + UPnP port mapping established on port %1 Transfert de port UPnP établi sur le port %1 - + UPnP port mapping removed Transfert de port UPnP retiré - + Mark all messages as read Marquer tous les messages comme lus - + Are you sure you would like to mark all messages read? Êtes-vous sûr(e) de vouloir marquer tous les messages comme lus ? - + Doing work necessary to send broadcast. Travail en cours afin d’envoyer la diffusion. - + Proof of work pending En attente de preuve de fonctionnement - + %n object(s) pending proof of work %n objet en attente de preuve de fonctionnement%n objet(s) en attente de preuve de fonctionnement - + %n object(s) waiting to be distributed %n objet en attente d'être distribué%n objet(s) en attente d'être distribués - + Wait until these tasks finish? Attendre jusqu'à ce que ces tâches se terminent ? - + Problem communicating with proxy: %1. Please check your network settings. Problème de communication avec le proxy : %1. Veuillez vérifier vos réglages réseau. - + SOCKS5 Authentication problem: %1. Please check your SOCKS5 settings. Problème d’authentification SOCKS5 : %1. Veuillez vérifier vos réglages SOCKS5. - + The time on your computer, %1, may be wrong. Please verify your settings. L'heure sur votre ordinateur, %1, pourrait être faussse. Veuillez vérifier vos paramètres. @@ -1404,12 +1404,17 @@ Difficulté requise du destinataire : %1 et %2 Ne pouvait pas comprendre NMControl. - + Your GPU(s) did not calculate correctly, disabling OpenCL. Please report to the developers. Votre GPU(s) n'a pas calculé correctement, mettant OpenCL hors service. Veuillez remonter ceci aux développeurs s'il vous plaît. - + + Set notification sound... + Mettre un son de notification ... + + + Welcome to easy and secure Bitmessage * send messages to other people @@ -1424,85 +1429,100 @@ Bienvenue dans le facile et sécurisé Bitmessage - + not recommended for chans pas recommandé pour les canaux - + Problems connecting? Try enabling UPnP in the Network Settings Des difficultés à se connecter ? Essayez de permettre UPnP dans les "Paramètres réseau" - + + You are trying to send an email instead of a bitmessage. This requires registering with a gateway. Attempt to register? + Vous essayez d'envoyer un courrier électronique au lieu d'un bitmessage. Ceci exige votre inscription à une passerelle. Essayer de vous inscrire ? + + + Error: Bitmessage addresses start with BM- Please check the recipient address %1 Erreur : Les adresses Bitmessage commencent par BM- Veuillez vérifier l'adresse du destinataire %1 - + Error: The recipient address %1 is not typed or copied correctly. Please check it. Erreur : L’adresse du destinataire %1 n’est pas correctement tapée ou recopiée. Veuillez la vérifier. - + Error: The recipient address %1 contains invalid characters. Please check it. Erreur : L’adresse du destinataire %1 contient des caractères invalides. Veuillez la vérifier. - + Error: The version of the recipient address %1 is too high. Either you need to upgrade your Bitmessage software or your acquaintance is being clever. Erreur : la version de l’adresse destinataire %1 est trop élevée. Vous devez mettre à niveau votre logiciel Bitmessage ou alors celui de votre connaissance est plus intelligent. - + Error: Some data encoded in the recipient address %1 is too short. There might be something wrong with the software of your acquaintance. Erreur : quelques données codées dans l’adresse destinataire %1 sont trop courtes. Il pourrait y avoir un soucis avec le logiciel de votre connaissance. - + Error: Some data encoded in the recipient address %1 is too long. There might be something wrong with the software of your acquaintance. Erreur : quelques données codées dans l’adresse destinataire %1 sont trop longues. Il pourrait y avoir un soucis avec le logiciel de votre connaissance. - + Error: Some data encoded in the recipient address %1 is malformed. There might be something wrong with the software of your acquaintance. Erreur : quelques données codées dans l’adresse destinataire %1 sont mal formées. Il pourrait y avoir un soucis avec le logiciel de votre connaissance. - + Error: Something is wrong with the recipient address %1. Erreur : quelque chose ne va pas avec l'adresse de destinataire %1. - + + From %1 + De %1 + + + Synchronisation pending En attente de synchronisation - + Bitmessage hasn't synchronised with the network, %n object(s) to be downloaded. If you quit now, it may cause delivery delays. Wait until the synchronisation finishes? Bitmessage ne s'est pas synchronisé avec le réseau, %n objet(s) à télécharger. Si vous quittez maintenant, cela pourrait causer des retards de livraison. Attendre jusqu'à ce que la synchronisation aboutisse ?Bitmessage ne s'est pas synchronisé avec le réseau, %n objet(s) à télécharger. Si vous quittez maintenant, cela pourrait causer des retards de livraison. Attendre jusqu'à ce que la synchronisation aboutisse ? - + Not connected Non connecté - + Bitmessage isn't connected to the network. If you quit now, it may cause delivery delays. Wait until connected and the synchronisation finishes? Bitmessage n'est pas connecté au réseau. Si vous quittez maintenant, cela pourrait causer des retards de livraison. Attendre jusqu'à ce qu'il soit connecté et que la synchronisation se termine ? - + Waiting for network connection... En attente de connexion réseau... - + Waiting for finishing synchronisation... En attente d'achèvement de la synchronisation... + + + You have already set a notification sound for this address book entry. Do you really want to overwrite it? + Vous avez déjà mis un son de notification pour cette adresse. Voulez-vous vraiment l’écraser ? + MessageView @@ -1530,14 +1550,14 @@ Bienvenue dans le facile et sécurisé Bitmessage MsgDecode - + The message has an unknown encoding. Perhaps you should upgrade Bitmessage. Le message est codé de façon inconnue. Peut-être que vous devriez mettre à niveau Bitmessage. - + Unknown encoding Encodage inconnu @@ -1845,77 +1865,77 @@ The 'Random Number' option is selected by default but deterministic ad Total de connexions: - + Since startup: Depuis le démarrage : - + Processed 0 person-to-person messages. Traité 0 messages de personne à personne. - + Processed 0 public keys. Traité 0 clés publiques. - + Processed 0 broadcasts. Traité 0 message de diffusion. - + Inventory lookups per second: 0 Consultations d’inventaire par seconde : 0 - + Objects to be synced: Objets à synchroniser : - + Stream # Flux N° Connections - Connexions + - + Since startup on %1 Démarré depuis le %1 - + Down: %1/s Total: %2 Téléchargées : %1/s Total : %2 - + Up: %1/s Total: %2 Téléversées : %1/s Total : %2 - + Total Connections: %1 Total des connexions : %1 - + Inventory lookups per second: %1 Consultations d’inventaire par seconde : %1 - + Up: 0 kB/s Téléversement : 0 kO/s - + Down: 0 kB/s Téléchargement : 0 kO/s @@ -1925,30 +1945,75 @@ The 'Random Number' option is selected by default but deterministic ad Statut du réseau - + byte(s) octetoctets - + Object(s) to be synced: %n Objet à synchroniser : %nObjets à synchroniser : %n - + Processed %n person-to-person message(s). Traité %n message de personne à personne.Traité %n messages de personne à personne. - + Processed %n broadcast message(s). Traité %n message de diffusion.Traité %n messages de diffusion. - + Processed %n public key(s). Traité %n clé publique.Traité %n clés publiques. + + + Peer + Pair + + + + IP address or hostname + Adresse IP ou nom d'hôte + + + + Rating + Évaluation + + + + PyBitmessage tracks the success rate of connection attempts to individual nodes. The rating ranges from -1 to 1 and affects the likelihood of selecting the node in the future + PyBitmessage suit à la trace le taux de réussites de connexions tentées vers les noeuds individuels. L'évaluation s'étend de -1 à 1 et affecte la probabilité de choisir ce noeud dans l'avenir + + + + User agent + Agent utilisateur + + + + Peer's self-reported software + Logiciel, auto-rapporté par le pair + + + + TLS + TLS + + + + Connection encryption + + + + + List of streams negotiated between you and the peer + + newChanDialog @@ -2044,17 +2109,17 @@ The 'Random Number' option is selected by default but deterministic ad proofofwork - + C PoW module built successfully. Module PoW C construit avec succès. - + Failed to build C PoW module. Please build it manually. Échec à construire le module PoW C. Veuillez le construire manuellement. - + C PoW module unavailable. Please build it. Module PoW C non disponible. Veuillez le construire. From 4c9006a632761ff63902307eba1b96d8ad39ff58 Mon Sep 17 00:00:00 2001 From: Peter Surda Date: Mon, 16 Oct 2017 08:07:32 +0200 Subject: [PATCH 239/407] Asyncore performance optimisation - use bytearray instead of strings for buffers --- src/network/advanceddispatcher.py | 25 ++++++++++++++++--------- src/network/bmproto.py | 18 +++++++++--------- src/network/downloadthread.py | 5 ++++- src/network/udp.py | 4 ++-- 4 files changed, 31 insertions(+), 21 deletions(-) diff --git a/src/network/advanceddispatcher.py b/src/network/advanceddispatcher.py index eb636aed..576eed39 100644 --- a/src/network/advanceddispatcher.py +++ b/src/network/advanceddispatcher.py @@ -12,8 +12,8 @@ class AdvancedDispatcher(asyncore.dispatcher): def __init__(self, sock=None): if not hasattr(self, '_map'): asyncore.dispatcher.__init__(self, sock) - self.read_buf = b"" - self.write_buf = b"" + self.read_buf = bytearray() + self.write_buf = bytearray() self.state = "init" self.lastTx = time.time() self.sentBytes = 0 @@ -25,18 +25,23 @@ class AdvancedDispatcher(asyncore.dispatcher): def append_write_buf(self, data): if data: - with self.writeLock: - self.write_buf += data + if isinstance(data, list): + with self.writeLock: + for chunk in data: + self.write_buf.extend(chunk) + else: + with self.writeLock: + self.write_buf.extend(data) def slice_write_buf(self, length=0): if length > 0: with self.writeLock: - self.write_buf = self.write_buf[length:] + del self.write_buf[0:length] def slice_read_buf(self, length=0): if length > 0: with self.readLock: - self.read_buf = self.read_buf[length:] + del self.read_buf[0:length] def process(self): if not self.connected: @@ -85,7 +90,7 @@ class AdvancedDispatcher(asyncore.dispatcher): self.receivedBytes += len(newData) asyncore.update_received(len(newData)) with self.readLock: - self.read_buf += newData + self.read_buf.extend(newData) def handle_write(self): self.lastTx = time.time() @@ -108,7 +113,9 @@ class AdvancedDispatcher(asyncore.dispatcher): return False def handle_close(self): - self.read_buf = b"" - self.write_buf = b"" + with self.readLock: + self.read_buf = bytearray() + with self.writeLock: + self.write_buf = bytearray() self.state = "close" asyncore.dispatcher.close(self) diff --git a/src/network/bmproto.py b/src/network/bmproto.py index 8099c5fc..a086fde0 100644 --- a/src/network/bmproto.py +++ b/src/network/bmproto.py @@ -138,16 +138,16 @@ class BMProto(AdvancedDispatcher, ObjectTracker): def decode_payload_node(self): services, host, port = self.decode_payload_content("Q16sH") if host[0:12] == '\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xFF\xFF': - host = socket.inet_ntop(socket.AF_INET, host[12:]) + host = socket.inet_ntop(socket.AF_INET, buffer(host, 12, 4)) elif host[0:6] == '\xfd\x87\xd8\x7e\xeb\x43': # Onion, based on BMD/bitcoind host = base64.b32encode(host[6:]).lower() + ".onion" else: - host = socket.inet_ntop(socket.AF_INET6, host) + host = socket.inet_ntop(socket.AF_INET6, buffer(host)) if host == "": # This can happen on Windows systems which are not 64-bit compatible # so let us drop the IPv6 address. - host = socket.inet_ntop(socket.AF_INET, host[12:]) + host = socket.inet_ntop(socket.AF_INET, buffer(host, 12, 4)) return Node(services, host, port) @@ -272,7 +272,7 @@ class BMProto(AdvancedDispatcher, ObjectTracker): return True #TODO make this more asynchronous random.shuffle(items) - for i in items: + for i in map(str, items): if i in DandelionStems().stem and \ self != DandelionStems().stem[i]: self.antiIntersectionDelay() @@ -298,7 +298,7 @@ class BMProto(AdvancedDispatcher, ObjectTracker): else: pass - for i in items: + for i in map(str, items): self.handleReceivedInventory(i) return True @@ -323,7 +323,7 @@ class BMProto(AdvancedDispatcher, ObjectTracker): self.dandelionRoutes = BMConnectionPool.dandelionRouteSelector(self) self.dandelionRefresh = time.time() + REASSIGN_INTERVAL - for i in items: + for i in map(str, items): DandelionStems().add(i, self, self.dandelionRoutes) self.handleReceivedInventory(i) @@ -354,12 +354,12 @@ class BMProto(AdvancedDispatcher, ObjectTracker): try: self.object.checkObjectByType() - objectProcessorQueue.put((self.object.objectType, self.object.data)) + objectProcessorQueue.put((self.object.objectType, buffer(self.object.data))) except BMObjectInvalidError as e: BMProto.stopDownloadingObject(self.object.inventoryHash, True) Inventory()[self.object.inventoryHash] = ( - self.object.objectType, self.object.streamNumber, self.payload[objectOffset:], self.object.expiresTime, self.object.tag) + self.object.objectType, self.object.streamNumber, buffer(self.payload[objectOffset:]), self.object.expiresTime, buffer(self.object.tag)) invQueue.put((self.object.streamNumber, self.object.inventoryHash, self.destination)) return True @@ -370,7 +370,7 @@ class BMProto(AdvancedDispatcher, ObjectTracker): addresses = self._decode_addr() for i in addresses: seenTime, stream, services, ip, port = i - decodedIP = protocol.checkIPAddress(ip) + decodedIP = protocol.checkIPAddress(buffer(ip)) if stream not in state.streamsInWhichIAmParticipating: continue if decodedIP is not False and seenTime > time.time() - BMProto.addressAlive: diff --git a/src/network/downloadthread.py b/src/network/downloadthread.py index 34f23eed..98b6df05 100644 --- a/src/network/downloadthread.py +++ b/src/network/downloadthread.py @@ -55,7 +55,10 @@ class DownloadThread(threading.Thread, StoppableThread): i.objectsNewToMe[k] = False self.pending[k] = now - payload = addresses.encodeVarint(len(request)) + ''.join(request) + payload = bytearray() + payload.extend(addresses.encodeVarint(len(request))) + for chunk in request: + payload.extend(chunk) i.append_write_buf(protocol.CreatePacket('getdata', payload)) logger.debug("%s:%i Requesting %i objects", i.destination.host, i.destination.port, len(request)) requested += len(request) diff --git a/src/network/udp.py b/src/network/udp.py index bd867125..3d238a5e 100644 --- a/src/network/udp.py +++ b/src/network/udp.py @@ -89,7 +89,7 @@ class UDPSocket(BMProto): remoteport = False for i in addresses: seenTime, stream, services, ip, port = i - decodedIP = protocol.checkIPAddress(ip) + decodedIP = protocol.checkIPAddress(buffer(ip)) if stream not in state.streamsInWhichIAmParticipating: continue if seenTime < time.time() - BMProto.maxTimeOffset or seenTime > time.time() + BMProto.maxTimeOffset: @@ -142,7 +142,7 @@ class UDPSocket(BMProto): else: self.local = False # overwrite the old buffer to avoid mixing data and so that self.local works correctly - self.read_buf = recdata + self.read_buf[0:] = recdata self.bm_proto_reset() receiveDataQueue.put(self.listening) From 9dae77dd2bc5c4104fdb5b9a53bd6f8ec151b0fc Mon Sep 17 00:00:00 2001 From: Dmitri Bogomolov <4glitch@gmail.com> Date: Thu, 12 Oct 2017 00:26:14 +0300 Subject: [PATCH 240/407] Quiet Mode (i.e. turn off notification) in tray menu --- src/bitmessageqt/__init__.py | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/src/bitmessageqt/__init__.py b/src/bitmessageqt/__init__.py index bb90bb47..0cedf8c7 100644 --- a/src/bitmessageqt/__init__.py +++ b/src/bitmessageqt/__init__.py @@ -864,6 +864,12 @@ class MyForm(settingsmixin.SMainWindow): self.actionShow.setChecked(False) self.appIndicatorShowOrHideWindow() + def appIndicatorSwitchQuietMode(self): + BMConfigParser().set( + 'bitmessagesettings', 'showtraynotifications', + str(not self.actionQuiet.isChecked()) + ) + # application indicator show or hide """# application indicator show or hide def appIndicatorShowBitmessage(self): @@ -1157,6 +1163,14 @@ class MyForm(settingsmixin.SMainWindow): if not sys.platform[0:3] == 'win': m.addAction(self.actionShow) + # quiet mode + self.actionQuiet = QtGui.QAction(_translate( + "MainWindow", "Quiet Mode"), m, checkable=True) + self.actionQuiet.setChecked(not BMConfigParser().getboolean( + 'bitmessagesettings', 'showtraynotifications')) + self.actionQuiet.triggered.connect(self.appIndicatorSwitchQuietMode) + m.addAction(self.actionQuiet) + # Send actionSend = QtGui.QAction(_translate( "MainWindow", "Send"), m, checkable=False) From 8e761693889b914ef52c4c27b3c57e28ff096c65 Mon Sep 17 00:00:00 2001 From: Dmitri Bogomolov <4glitch@gmail.com> Date: Sat, 7 Oct 2017 17:28:32 +0300 Subject: [PATCH 241/407] Check IP before adding to knownnodes in helper_bootstrap --- src/helper_bootstrap.py | 48 ++++++++++++++++++++++++++--------------- 1 file changed, 31 insertions(+), 17 deletions(-) diff --git a/src/helper_bootstrap.py b/src/helper_bootstrap.py index 7c7456e1..0ba86348 100644 --- a/src/helper_bootstrap.py +++ b/src/helper_bootstrap.py @@ -9,6 +9,7 @@ import knownnodes import socks import state + def addKnownNode(stream, peer, lastseen=None, self=False): if lastseen is None: lastseen = time.time() @@ -18,6 +19,7 @@ def addKnownNode(stream, peer, lastseen=None, self=False): "self": self, } + def knownNodes(): try: with open(state.appdata + 'knownnodes.dat', 'rb') as pickleFile: @@ -38,26 +40,37 @@ def knownNodes(): logger.error('Bitmessage cannot read future versions of the keys file (keys.dat). Run the newer version of Bitmessage.') raise SystemExit + def dns(): # DNS bootstrap. This could be programmed to use the SOCKS proxy to do the # DNS lookup some day but for now we will just rely on the entries in # defaultKnownNodes.py. Hopefully either they are up to date or the user # has run Bitmessage recently without SOCKS turned on and received good # bootstrap nodes using that method. - if BMConfigParser().get('bitmessagesettings', 'socksproxytype') == 'none': + def try_add_known_node(stream, addr, port, method=''): try: - for item in socket.getaddrinfo('bootstrap8080.bitmessage.org', 80): - logger.info('Adding ' + item[4][0] + ' to knownNodes based on DNS bootstrap method') - addKnownNode(1, state.Peer(item[4][0], 8080)) - except: - logger.error('bootstrap8080.bitmessage.org DNS bootstrapping failed.') - try: - for item in socket.getaddrinfo('bootstrap8444.bitmessage.org', 80): - logger.info ('Adding ' + item[4][0] + ' to knownNodes based on DNS bootstrap method') - addKnownNode(1, state.Peer(item[4][0], 8444)) - except: - logger.error('bootstrap8444.bitmessage.org DNS bootstrapping failed.') - elif BMConfigParser().get('bitmessagesettings', 'socksproxytype') == 'SOCKS5': + socket.inet_aton(addr) + except (TypeError, socket.error): + return + logger.info( + 'Adding %s to knownNodes based on %s DNS bootstrap method', + addr, method) + addKnownNode(stream, state.Peer(addr, port)) + + proxy_type = BMConfigParser().get('bitmessagesettings', 'socksproxytype') + + if proxy_type == 'none': + for port in [8080, 8444]: + try: + for item in socket.getaddrinfo( + 'bootstrap%s.bitmessage.org' % port, 80): + try_add_known_node(1, item[4][0], port) + except: + logger.error( + 'bootstrap%s.bitmessage.org DNS bootstrapping failed.', + port, exc_info=True + ) + elif proxy_type == 'SOCKS5': addKnownNode(1, state.Peer('quzwelsuziwqgpt2.onion', 8444)) logger.debug("Adding quzwelsuziwqgpt2.onion:8444 to knownNodes.") for port in [8080, 8444]: @@ -89,8 +102,9 @@ def dns(): except: logger.error("SOCKS DNS resolving failed", exc_info=True) else: - if ip is not None: - logger.info ('Adding ' + ip + ' to knownNodes based on SOCKS DNS bootstrap method') - addKnownNode(1, state.Peer(ip, port)) + try_add_known_node(1, ip, port, 'SOCKS') else: - logger.info('DNS bootstrap skipped because the proxy type does not support DNS resolution.') + logger.info( + 'DNS bootstrap skipped because the proxy type does not support' + ' DNS resolution.' + ) From 59d1309a9e4c389cf6b21c9786490c704705b2a5 Mon Sep 17 00:00:00 2001 From: Dmitri Bogomolov <4glitch@gmail.com> Date: Mon, 9 Oct 2017 12:09:09 +0300 Subject: [PATCH 242/407] Fixed typo when closing tempfile in bitmessageqt.__init__ --- src/bitmessageqt/__init__.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/bitmessageqt/__init__.py b/src/bitmessageqt/__init__.py index 0cedf8c7..7e945229 100644 --- a/src/bitmessageqt/__init__.py +++ b/src/bitmessageqt/__init__.py @@ -3994,8 +3994,9 @@ class settingsDialog(QtGui.QDialog): else: try: import tempfile - file = tempfile.NamedTemporaryFile(dir=paths.lookupExeFolder(), delete=True) - file.close # should autodelete + tempfile.NamedTemporaryFile( + dir=paths.lookupExeFolder(), delete=True + ).close() # should autodelete except: self.ui.checkBoxPortableMode.setDisabled(True) From 9cffd50de8580577850099f8611b367a0c84a8d6 Mon Sep 17 00:00:00 2001 From: Dmitri Bogomolov <4glitch@gmail.com> Date: Thu, 12 Oct 2017 16:13:59 +0300 Subject: [PATCH 243/407] Fixed statusbar message on error in namecoin name search --- src/bitmessageqt/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/bitmessageqt/__init__.py b/src/bitmessageqt/__init__.py index 7e945229..4d2c0bad 100644 --- a/src/bitmessageqt/__init__.py +++ b/src/bitmessageqt/__init__.py @@ -1999,7 +1999,7 @@ class MyForm(settingsmixin.SMainWindow): err, addr = nc.query(identities[-1].strip()) if err is not None: self.statusBar().showMessage(_translate( - "MainWindow", "Error: " + err), 10000) + "MainWindow", "Error: %1").arg(err), 10000) else: identities[-1] = addr self.ui.lineEditTo.setText("; ".join(identities)) From 7e0932815dbd0e0f8e181c815014bcce560cc5eb Mon Sep 17 00:00:00 2001 From: Peter Surda Date: Thu, 19 Oct 2017 01:46:32 +0200 Subject: [PATCH 244/407] UDP socket closing fix - invalid data or an incomplete read on UDP socket caused it to close --- src/network/bmproto.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/network/bmproto.py b/src/network/bmproto.py index a086fde0..fb0fea30 100644 --- a/src/network/bmproto.py +++ b/src/network/bmproto.py @@ -114,6 +114,9 @@ class BMProto(AdvancedDispatcher, ObjectTracker): logger.debug("%s:%i already got object, skipping", self.destination.host, self.destination.port) except struct.error: logger.debug("decoding error, skipping") + elif self.socket.type == socket.SOCK_DGRAM: + # broken read, ignore + pass else: #print "Skipping command %s due to invalid data" % (self.command) logger.debug("Closing due to invalid command %s", self.command) From 1ba1f026866ebd163d260201843eb87051065e4c Mon Sep 17 00:00:00 2001 From: Peter Surda Date: Thu, 19 Oct 2017 08:31:05 +0200 Subject: [PATCH 245/407] Remove superfluous validator signal connect in newchandialog - apparently it connects automatically and just creates an error message --- src/bitmessageqt/newchandialog.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/bitmessageqt/newchandialog.py b/src/bitmessageqt/newchandialog.py index b28d2bec..a129c608 100644 --- a/src/bitmessageqt/newchandialog.py +++ b/src/bitmessageqt/newchandialog.py @@ -15,8 +15,6 @@ class NewChanDialog(QtGui.QDialog, RetranslateMixin): self.parent = parent self.chanAddress.setValidator(AddressValidator(self.chanAddress, self.chanPassPhrase, self.validatorFeedback, self.buttonBox, False)) self.chanPassPhrase.setValidator(PassPhraseValidator(self.chanPassPhrase, self.chanAddress, self.validatorFeedback, self.buttonBox, False)) - QtCore.QObject.connect(self.chanAddress, QtCore.SIGNAL('textEdited()'), self.chanAddress.validator(), QtCore.SLOT('checkData(self)')) - QtCore.QObject.connect(self.chanPassPhrase, QtCore.SIGNAL('textEdited()'), self.chanPassPhrase.validator(), QtCore.SLOT('checkData(self)')) self.timer = QtCore.QTimer() QtCore.QObject.connect(self.timer, QtCore.SIGNAL("timeout()"), self.delayedUpdateStatus) From a013814c6b244dfb8009411572bd5b56f47d6dee Mon Sep 17 00:00:00 2001 From: Peter Surda Date: Thu, 19 Oct 2017 08:39:09 +0200 Subject: [PATCH 246/407] Network tab updates - handle add/remove entry instead of recreating the whole connection list - update processed object counts after each object --- src/bitmessageqt/networkstatus.py | 67 ++++++++++++++++++++----------- src/bitmessageqt/uisignaler.py | 3 +- src/class_objectProcessor.py | 12 +++--- src/network/tcp.py | 5 ++- 4 files changed, 54 insertions(+), 33 deletions(-) diff --git a/src/bitmessageqt/networkstatus.py b/src/bitmessageqt/networkstatus.py index 2609a3c4..e9073cb8 100644 --- a/src/bitmessageqt/networkstatus.py +++ b/src/bitmessageqt/networkstatus.py @@ -11,6 +11,7 @@ from retranslateui import RetranslateMixin from uisignaler import UISignaler import widgets +from network.connectionpool import BMConnectionPool class NetworkStatus(QtGui.QWidget, RetranslateMixin): def __init__(self, parent=None): @@ -31,7 +32,7 @@ class NetworkStatus(QtGui.QWidget, RetranslateMixin): QtCore.QObject.connect(self.UISignalThread, QtCore.SIGNAL( "updateNumberOfBroadcastsProcessed()"), self.updateNumberOfBroadcastsProcessed) QtCore.QObject.connect(self.UISignalThread, QtCore.SIGNAL( - "updateNetworkStatusTab()"), self.updateNetworkStatusTab) + "updateNetworkStatusTab(PyQt_PyObject,PyQt_PyObject,PyQt_PyObject)"), self.updateNetworkStatusTab) self.timer = QtCore.QTimer() self.timer.start(2000) # milliseconds @@ -52,17 +53,17 @@ class NetworkStatus(QtGui.QWidget, RetranslateMixin): self.labelSyncStatus.setText(_translate("networkstatus", "Object(s) to be synced: %n", None, QtCore.QCoreApplication.CodecForTr, network.stats.pendingDownload() + network.stats.pendingUpload())) def updateNumberOfMessagesProcessed(self): -# self.updateNumberOfObjectsToBeSynced() + self.updateNumberOfObjectsToBeSynced() self.labelMessageCount.setText(_translate( "networkstatus", "Processed %n person-to-person message(s).", None, QtCore.QCoreApplication.CodecForTr, shared.numberOfMessagesProcessed)) def updateNumberOfBroadcastsProcessed(self): -# self.updateNumberOfObjectsToBeSynced() + self.updateNumberOfObjectsToBeSynced() self.labelBroadcastCount.setText(_translate( "networkstatus", "Processed %n broadcast message(s).", None, QtCore.QCoreApplication.CodecForTr, shared.numberOfBroadcastsProcessed)) def updateNumberOfPubkeysProcessed(self): -# self.updateNumberOfObjectsToBeSynced() + self.updateNumberOfObjectsToBeSynced() self.labelPubkeyCount.setText(_translate( "networkstatus", "Processed %n public key(s).", None, QtCore.QCoreApplication.CodecForTr, shared.numberOfPubkeysProcessed)) @@ -76,50 +77,71 @@ class NetworkStatus(QtGui.QWidget, RetranslateMixin): self.labelBytesSentCount.setText(_translate( "networkstatus", "Up: %1/s Total: %2").arg(self.formatByteRate(network.stats.uploadSpeed()), self.formatBytes(network.stats.sentBytes()))) - def updateNetworkStatusTab(self): - connectedHosts = network.stats.connectedHostsList() + def updateNetworkStatusTab(self, outbound, add, destination): + if outbound: + try: + c = BMConnectionPool().outboundConnections[destination] + except KeyError: + if add: + return + else: + try: + c = BMConnectionPool().inboundConnections[destination] + except KeyError: + try: + c = BMConnectionPool().inboundConnections[destination.host] + except KeyError: + if add: + return self.tableWidgetConnectionCount.setUpdatesEnabled(False) - #self.tableWidgetConnectionCount.setSortingEnabled(False) - #self.tableWidgetConnectionCount.clearContents() - self.tableWidgetConnectionCount.setRowCount(0) - for i in connectedHosts: + self.tableWidgetConnectionCount.setSortingEnabled(False) + if add: self.tableWidgetConnectionCount.insertRow(0) self.tableWidgetConnectionCount.setItem(0, 0, - QtGui.QTableWidgetItem("%s:%i" % (i.destination.host, i.destination.port)) + QtGui.QTableWidgetItem("%s:%i" % (destination.host, destination.port)) ) self.tableWidgetConnectionCount.setItem(0, 2, - QtGui.QTableWidgetItem("%s" % (i.userAgent)) + QtGui.QTableWidgetItem("%s" % (c.userAgent)) ) self.tableWidgetConnectionCount.setItem(0, 3, - QtGui.QTableWidgetItem("%s" % (i.tlsVersion)) + QtGui.QTableWidgetItem("%s" % (c.tlsVersion)) ) self.tableWidgetConnectionCount.setItem(0, 4, - QtGui.QTableWidgetItem("%s" % (",".join(map(str,i.streams)))) + QtGui.QTableWidgetItem("%s" % (",".join(map(str,c.streams)))) ) try: # FIXME hard coded stream no - rating = knownnodes.knownNodes[1][i.destination]['rating'] + rating = "%.1f" % (knownnodes.knownNodes[1][destination]['rating']) except KeyError: rating = "-" self.tableWidgetConnectionCount.setItem(0, 1, QtGui.QTableWidgetItem("%s" % (rating)) ) - if i.isOutbound: + if outbound: brush = QtGui.QBrush(QtGui.QColor("yellow"), QtCore.Qt.SolidPattern) else: brush = QtGui.QBrush(QtGui.QColor("green"), QtCore.Qt.SolidPattern) for j in (range(1)): self.tableWidgetConnectionCount.item(0, j).setBackground(brush) + self.tableWidgetConnectionCount.item(0, 0).setData(QtCore.Qt.UserRole, destination) + self.tableWidgetConnectionCount.item(0, 1).setData(QtCore.Qt.UserRole, outbound) + else: + for i in range(self.tableWidgetConnectionCount.rowCount()): + if self.tableWidgetConnectionCount.item(i, 0).data(QtCore.Qt.UserRole).toPyObject() != destination: + continue + if self.tableWidgetConnectionCount.item(i, 1).data(QtCore.Qt.UserRole).toPyObject() == outbound: + self.tableWidgetConnectionCount.removeRow(i) + break self.tableWidgetConnectionCount.setUpdatesEnabled(True) - #self.tableWidgetConnectionCount.setSortingEnabled(True) - #self.tableWidgetConnectionCount.horizontalHeader().setSortIndicator(1, QtCore.Qt.AscendingOrder) + self.tableWidgetConnectionCount.setSortingEnabled(True) + self.tableWidgetConnectionCount.horizontalHeader().setSortIndicator(0, QtCore.Qt.AscendingOrder) self.labelTotalConnections.setText(_translate( - "networkstatus", "Total Connections: %1").arg(str(len(connectedHosts)))) + "networkstatus", "Total Connections: %1").arg(str(self.tableWidgetConnectionCount.rowCount()))) # FYI: The 'singlelistener' thread sets the icon color to green when it receives an incoming connection, meaning that the user's firewall is configured correctly. - if connectedHosts and shared.statusIconColor == 'red': + if self.tableWidgetConnectionCount.rowCount() and shared.statusIconColor == 'red': self.window().setStatusIcon('yellow') - elif not connectedHosts and shared.statusIconColor != "red": + elif self.tableWidgetConnectionCount.rowCount() == 0 and shared.statusIconColor != "red": self.window().setStatusIcon('red') # timer driven @@ -129,9 +151,6 @@ class NetworkStatus(QtGui.QWidget, RetranslateMixin): Inventory().numberOfInventoryLookupsPerformed = 0 self.updateNumberOfBytes() self.updateNumberOfObjectsToBeSynced() - self.updateNumberOfMessagesProcessed() - self.updateNumberOfBroadcastsProcessed() - self.updateNumberOfPubkeysProcessed() def retranslateUi(self): super(NetworkStatus, self).retranslateUi() diff --git a/src/bitmessageqt/uisignaler.py b/src/bitmessageqt/uisignaler.py index aabfaf68..055f9097 100644 --- a/src/bitmessageqt/uisignaler.py +++ b/src/bitmessageqt/uisignaler.py @@ -45,7 +45,8 @@ class UISignaler(QThread): "displayNewSentMessage(PyQt_PyObject,PyQt_PyObject,PyQt_PyObject,PyQt_PyObject,PyQt_PyObject,PyQt_PyObject)"), toAddress, fromLabel, fromAddress, subject, message, ackdata) elif command == 'updateNetworkStatusTab': - self.emit(SIGNAL("updateNetworkStatusTab()")) + outbound, add, destination = data + self.emit(SIGNAL("updateNetworkStatusTab(PyQt_PyObject,PyQt_PyObject,PyQt_PyObject)"), outbound, add, destination) elif command == 'updateNumberOfMessagesProcessed': self.emit(SIGNAL("updateNumberOfMessagesProcessed()")) elif command == 'updateNumberOfPubkeysProcessed': diff --git a/src/class_objectProcessor.py b/src/class_objectProcessor.py index e59645bc..ec2b32b9 100644 --- a/src/class_objectProcessor.py +++ b/src/class_objectProcessor.py @@ -194,8 +194,8 @@ class objectProcessor(threading.Thread): def processpubkey(self, data): pubkeyProcessingStartTime = time.time() shared.numberOfPubkeysProcessed += 1 -# queues.UISignalQueue.put(( -# 'updateNumberOfPubkeysProcessed', 'no data')) + queues.UISignalQueue.put(( + 'updateNumberOfPubkeysProcessed', 'no data')) embeddedTime, = unpack('>Q', data[8:16]) readPosition = 20 # bypass the nonce, time, and object type addressVersion, varintLength = decodeVarint( @@ -343,8 +343,8 @@ class objectProcessor(threading.Thread): def processmsg(self, data): messageProcessingStartTime = time.time() shared.numberOfMessagesProcessed += 1 -# queues.UISignalQueue.put(( -# 'updateNumberOfMessagesProcessed', 'no data')) + queues.UISignalQueue.put(( + 'updateNumberOfMessagesProcessed', 'no data')) readPosition = 20 # bypass the nonce, time, and object type msgVersion, msgVersionLength = decodeVarint(data[readPosition:readPosition + 9]) if msgVersion != 1: @@ -613,8 +613,8 @@ class objectProcessor(threading.Thread): def processbroadcast(self, data): messageProcessingStartTime = time.time() shared.numberOfBroadcastsProcessed += 1 -# queues.UISignalQueue.put(( -# 'updateNumberOfBroadcastsProcessed', 'no data')) + queues.UISignalQueue.put(( + 'updateNumberOfBroadcastsProcessed', 'no data')) inventoryHash = calculateInventoryHash(data) readPosition = 20 # bypass the nonce, time, and object type broadcastVersion, broadcastVersionLength = decodeVarint( diff --git a/src/network/tcp.py b/src/network/tcp.py index 60acb22c..dd813ac6 100644 --- a/src/network/tcp.py +++ b/src/network/tcp.py @@ -72,7 +72,6 @@ class TCPConnection(BMProto, TLSDispatcher): self.local = False #shared.connectedHostsList[self.destination] = 0 ObjectTracker.__init__(self) - UISignalQueue.put(('updateNetworkStatusTab', 'no data')) self.bm_proto_reset() self.set_state("bm_header", expectBytes=protocol.Header.size) @@ -102,7 +101,7 @@ class TCPConnection(BMProto, TLSDispatcher): if not self.isOutbound and not self.local: shared.clientHasReceivedIncomingConnections = True UISignalQueue.put(('setStatusIcon', 'green')) - UISignalQueue.put(('updateNetworkStatusTab', 'no data')) + UISignalQueue.put(('updateNetworkStatusTab', (self.isOutbound, True, self.destination))) self.antiIntersectionDelay(True) self.fullyEstablished = True if self.isOutbound: @@ -218,6 +217,8 @@ class TCPConnection(BMProto, TLSDispatcher): if self.isOutbound and not self.fullyEstablished: knownnodes.decreaseRating(self.destination) BMProto.handle_close(self, reason) + if self.fullyEstablished: + UISignalQueue.put(('updateNetworkStatusTab', (self.isOutbound, False, self.destination))) class Socks5BMConnection(Socks5Connection, TCPConnection): From 453bcdbb1d29baad80a8fa75e714a3ecd598dcaf Mon Sep 17 00:00:00 2001 From: Peter Surda Date: Thu, 19 Oct 2017 08:39:30 +0200 Subject: [PATCH 247/407] Typo in newchandialog --- src/bitmessageqt/newchandialog.ui | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/bitmessageqt/newchandialog.ui b/src/bitmessageqt/newchandialog.ui index 0abe061c..59dbb2bb 100644 --- a/src/bitmessageqt/newchandialog.ui +++ b/src/bitmessageqt/newchandialog.ui @@ -53,7 +53,7 @@ - Chan passhphrase/name: + Chan passphrase/name: From d44c6c6464863e1cc0dfb87a247a71801cf67e85 Mon Sep 17 00:00:00 2001 From: Peter Surda Date: Thu, 19 Oct 2017 08:52:44 +0200 Subject: [PATCH 248/407] Forget known nodes with bad rating --- src/class_singleCleaner.py | 14 +++++++++++--- src/knownnodes.py | 8 ++++++-- src/network/bmproto.py | 9 ++++++--- 3 files changed, 23 insertions(+), 8 deletions(-) diff --git a/src/class_singleCleaner.py b/src/class_singleCleaner.py index 2e4140b6..3068910d 100644 --- a/src/class_singleCleaner.py +++ b/src/class_singleCleaner.py @@ -96,16 +96,24 @@ class singleCleaner(threading.Thread, StoppableThread): # cleanup old nodes now = int(time.time()) - toDelete = [] with knownnodes.knownNodesLock: for stream in knownnodes.knownNodes: - for node in knownnodes.knownNodes[stream].keys(): + keys = knownnodes.knownNodes[stream].keys() + for node in keys: try: + # scrap old nodes if now - knownnodes.knownNodes[stream][node]["lastseen"] > 2419200: # 28 days - shared.needToWriteKownNodesToDisk = True + shared.needToWriteKnownNodesToDisk = True del knownnodes.knownNodes[stream][node] + continue + # scrap old nodes with low rating + if now - knownnodes.knownNodes[stream][node]["lastseen"] > 10800 and knownnodes.knownNodes[stream][node]["rating"] <= knownnodes.knownNodesForgetRating: + shared.needToWriteKnownNodesToDisk = True + del knownnodes.knownNodes[stream][node] + continue except TypeError: print "Error in %s" % (str(node)) + keys = [] # Let us write out the knowNodes to disk if there is anything new to write out. if shared.needToWriteKnownNodesToDisk: diff --git a/src/knownnodes.py b/src/knownnodes.py index 86d39cbe..aa080128 100644 --- a/src/knownnodes.py +++ b/src/knownnodes.py @@ -1,4 +1,5 @@ import pickle +import os import threading from bmconfigparser import BMConfigParser @@ -9,11 +10,14 @@ knownNodes = {} knownNodesTrimAmount = 2000 +# forget a node after rating is this low +knownNodesForgetRating = -0.5 + def saveKnownNodes(dirName = None): if dirName is None: dirName = state.appdata with knownNodesLock: - with open(dirName + 'knownnodes.dat', 'wb') as output: + with open(os.path.join(dirName, 'knownnodes.dat'), 'wb') as output: pickle.dump(knownNodes, output) def increaseRating(peer): @@ -37,7 +41,7 @@ def decreaseRating(peer): pass def trimKnownNodes(recAddrStream = 1): - if len(knownNodes[recAddrStream]) < BMConfigParser().get("knownnodes", "maxnodes"): + if len(knownNodes[recAddrStream]) < int(BMConfigParser().get("knownnodes", "maxnodes")): return with knownNodesLock: oldestList = sorted(knownNodes[recAddrStream], key=lambda x: x['lastseen'])[:knownNodesTrimAmount] diff --git a/src/network/bmproto.py b/src/network/bmproto.py index fb0fea30..c245a675 100644 --- a/src/network/bmproto.py +++ b/src/network/bmproto.py @@ -378,9 +378,12 @@ class BMProto(AdvancedDispatcher, ObjectTracker): continue if decodedIP is not False and seenTime > time.time() - BMProto.addressAlive: peer = state.Peer(decodedIP, port) - if peer in knownnodes.knownNodes[stream] and knownnodes.knownNodes[stream][peer]["lastseen"] > seenTime: - continue - if len(knownnodes.knownNodes[stream]) < BMConfigParser().get("knownnodes", "maxnodes"): + try: + if knownnodes.knownNodes[stream][peer]["lastseen"] > seenTime: + continue + except KeyError: + pass + if len(knownnodes.knownNodes[stream]) < int(BMConfigParser().get("knownnodes", "maxnodes")): with knownnodes.knownNodesLock: try: knownnodes.knownNodes[stream][peer]["lastseen"] = seenTime From a090eea9b03ad7316e471743e1e61a411172bcc1 Mon Sep 17 00:00:00 2001 From: Peter Surda Date: Thu, 19 Oct 2017 08:56:48 +0200 Subject: [PATCH 249/407] Minor multiqueue updates - add task_done to addrthread and invthread - implement totalSize for multiqueue - order in invThread changed --- src/multiqueue.py | 6 ++++-- src/network/addrthread.py | 2 ++ src/network/invthread.py | 4 +++- 3 files changed, 9 insertions(+), 3 deletions(-) diff --git a/src/multiqueue.py b/src/multiqueue.py index 30326ee7..62b0fa87 100644 --- a/src/multiqueue.py +++ b/src/multiqueue.py @@ -24,8 +24,7 @@ class MultiQueue(Queue.Queue): # Put a new item in the queue def _put(self, item): #self.queue.append(item) - i = random.randrange(0, self.queueCount) - self.queues[i].append((item)) + self.queues[random.randrange(self.queueCount)].append((item)) # Get an item from the queue def _get(self): @@ -33,3 +32,6 @@ class MultiQueue(Queue.Queue): def iterate(self): self.iter = (self.iter + 1) % self.queueCount + + def totalSize(self): + return sum(len(x) for x in self.queues) diff --git a/src/network/addrthread.py b/src/network/addrthread.py index 8c78894f..5b0ea638 100644 --- a/src/network/addrthread.py +++ b/src/network/addrthread.py @@ -31,4 +31,6 @@ class AddrThread(threading.Thread, StoppableThread): #finish addrQueue.iterate() + for i in range(len(chunk)): + addrQueue.task_done() self.stop.wait(1) diff --git a/src/network/invthread.py b/src/network/invthread.py index cbed7a70..4f26c0fa 100644 --- a/src/network/invthread.py +++ b/src/network/invthread.py @@ -33,6 +33,7 @@ class InvThread(threading.Thread, StoppableThread): self.dandelionLocalRouteRefresh() try: data = invQueue.get(False) + chunk.append((data[0], data[1])) # locally generated if len(data) == 2: DandelionStems().add(data[1], None, self.dandelionRoutes) @@ -41,7 +42,6 @@ class InvThread(threading.Thread, StoppableThread): else: source = BMConnectionPool().getConnectionByAddr(data[2]) BMConnectionPool().handleReceivedObject(data[0], data[1], source) - chunk.append((data[0], data[1])) except Queue.Empty: break # connection not found, handle it as if generated locally @@ -81,4 +81,6 @@ class InvThread(threading.Thread, StoppableThread): connection.append_write_buf(protocol.CreatePacket('dinv', \ addresses.encodeVarint(len(stems)) + "".join(stems))) invQueue.iterate() + for i in range(len(chunk)): + invQueue.task_done() self.stop.wait(1) From f785558ca556d7b3b3968c326db31d0ee6c06181 Mon Sep 17 00:00:00 2001 From: Peter Surda Date: Thu, 19 Oct 2017 09:00:02 +0200 Subject: [PATCH 250/407] Don't close UDP socket on bad packet magic --- src/network/bmproto.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/network/bmproto.py b/src/network/bmproto.py index c245a675..3ae7b635 100644 --- a/src/network/bmproto.py +++ b/src/network/bmproto.py @@ -74,7 +74,9 @@ class BMProto(AdvancedDispatcher, ObjectTracker): self.set_state("bm_header", length=1) self.bm_proto_reset() logger.debug("Bad magic") - self.handle_close("Bad magic") + if self.socket.type == socket.SOCK_STREAM: + self.close_reason = "Bad magic" + self.set_state("close") return False if self.payloadLength > BMProto.maxMessageSize: self.invalid = True From 7ec3fc7a5ab235c04d1dc09ce514f45bed11cb25 Mon Sep 17 00:00:00 2001 From: Peter Surda Date: Thu, 19 Oct 2017 09:00:54 +0200 Subject: [PATCH 251/407] Prevent negative DownloadChunk in asyncore --- src/network/advanceddispatcher.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/network/advanceddispatcher.py b/src/network/advanceddispatcher.py index 576eed39..331daf03 100644 --- a/src/network/advanceddispatcher.py +++ b/src/network/advanceddispatcher.py @@ -79,6 +79,8 @@ class AdvancedDispatcher(asyncore.dispatcher): try: if self.expectBytes > 0 and not self.fullyEstablished: self.downloadChunk = min(self.downloadChunk, self.expectBytes - len(self.read_buf)) + if self.downloadChunk < 0: + self.downloadChunk = 0 except AttributeError: pass return asyncore.dispatcher.readable(self) and \ From d28a7bfb862e6d9b9716098b27e5ec1d2f482da8 Mon Sep 17 00:00:00 2001 From: Peter Surda Date: Thu, 19 Oct 2017 09:02:33 +0200 Subject: [PATCH 252/407] Asyncore performance optimisation - don't transfer unnecessary amount of bytes from network buffers - slice buffer more efficiently if it results in an empty buffer --- src/network/advanceddispatcher.py | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/src/network/advanceddispatcher.py b/src/network/advanceddispatcher.py index 331daf03..97481238 100644 --- a/src/network/advanceddispatcher.py +++ b/src/network/advanceddispatcher.py @@ -36,12 +36,18 @@ class AdvancedDispatcher(asyncore.dispatcher): def slice_write_buf(self, length=0): if length > 0: with self.writeLock: - del self.write_buf[0:length] + if length >= len(self.write_buf): + del self.write_buf[:] + else: + del self.write_buf[0:length] def slice_read_buf(self, length=0): if length > 0: with self.readLock: - del self.read_buf[0:length] + if length >= len(self.read_buf): + del self.read_buf[:] + else: + del self.read_buf[0:length] def process(self): if not self.connected: @@ -77,7 +83,7 @@ class AdvancedDispatcher(asyncore.dispatcher): if asyncore.maxDownloadRate > 0: self.downloadChunk = asyncore.downloadBucket try: - if self.expectBytes > 0 and not self.fullyEstablished: + if self.expectBytes > 0: self.downloadChunk = min(self.downloadChunk, self.expectBytes - len(self.read_buf)) if self.downloadChunk < 0: self.downloadChunk = 0 From 01d46c30e49dcc889105885de37ca0bc8a5d7187 Mon Sep 17 00:00:00 2001 From: Peter Surda Date: Thu, 19 Oct 2017 09:03:36 +0200 Subject: [PATCH 253/407] Remove obsolete imports --- src/shutdown.py | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/src/shutdown.py b/src/shutdown.py index a066104c..278759e5 100644 --- a/src/shutdown.py +++ b/src/shutdown.py @@ -3,8 +3,6 @@ import Queue import threading import time -from class_outgoingSynSender import outgoingSynSender -from class_sendDataThread import sendDataThread from bmconfigparser import BMConfigParser from debug import logger from helper_sql import sqlQuery, sqlStoredProcedure @@ -51,9 +49,7 @@ def doCleanShutdown(): time.sleep(.25) for thread in threading.enumerate(): - if isinstance(thread, sendDataThread): - thread.sendDataThreadQueue.put((0, 'shutdown','no data')) - if thread is not threading.currentThread() and isinstance(thread, StoppableThread) and not isinstance(thread, outgoingSynSender): + if thread is not threading.currentThread() and isinstance(thread, StoppableThread): logger.debug("Waiting for thread %s", thread.name) thread.join() From 391d40d78bd980db75caecb156496f7ce045c08a Mon Sep 17 00:00:00 2001 From: Peter Surda Date: Thu, 19 Oct 2017 09:08:05 +0200 Subject: [PATCH 254/407] Socket closing changes - closing reason moved to a variable - actual closing now done in asyncore thread instead of receivedata thread --- src/network/advanceddispatcher.py | 2 +- src/network/bmproto.py | 13 +++++++------ src/network/connectionpool.py | 5 ++++- src/network/socks4a.py | 3 ++- src/network/socks5.py | 3 ++- src/network/tcp.py | 4 ++-- 6 files changed, 18 insertions(+), 12 deletions(-) diff --git a/src/network/advanceddispatcher.py b/src/network/advanceddispatcher.py index 97481238..2115c454 100644 --- a/src/network/advanceddispatcher.py +++ b/src/network/advanceddispatcher.py @@ -126,4 +126,4 @@ class AdvancedDispatcher(asyncore.dispatcher): with self.writeLock: self.write_buf = bytearray() self.state = "close" - asyncore.dispatcher.close(self) + self.close() diff --git a/src/network/bmproto.py b/src/network/bmproto.py index 3ae7b635..d66d8c4b 100644 --- a/src/network/bmproto.py +++ b/src/network/bmproto.py @@ -1,9 +1,9 @@ import base64 import hashlib -import time import random import socket import struct +import time from bmconfigparser import BMConfigParser from debug import logger @@ -122,7 +122,8 @@ class BMProto(AdvancedDispatcher, ObjectTracker): else: #print "Skipping command %s due to invalid data" % (self.command) logger.debug("Closing due to invalid command %s", self.command) - self.handle_close("Invalid command %s" % (self.command)) + self.close_reason = "Invalid command %s" % (self.command) + self.set_state("close") return False if retval: self.set_state("bm_header", length=self.payloadLength) @@ -538,13 +539,13 @@ class BMProto(AdvancedDispatcher, ObjectTracker): except KeyError: pass - def handle_close(self, reason=None): + def handle_close(self): self.set_state("close") - if reason is None: + try: + logger.debug("%s:%i: closing, %s", self.destination.host, self.destination.port, self.close_reason) + except AttributeError: try: logger.debug("%s:%i: closing", self.destination.host, self.destination.port) except AttributeError: logger.debug("Disconnected socket closing") - else: - logger.debug("%s:%i: closing, %s", self.destination.host, self.destination.port, reason) AdvancedDispatcher.handle_close(self) diff --git a/src/network/connectionpool.py b/src/network/connectionpool.py index 7ae8afd9..1e50eb1c 100644 --- a/src/network/connectionpool.py +++ b/src/network/connectionpool.py @@ -247,8 +247,11 @@ class BMConnectionPool(object): if i.fullyEstablished: i.append_write_buf(protocol.CreatePacket('ping')) else: - i.handle_close("Timeout (%is)" % (time.time() - i.lastTx)) + i.close_reason = "Timeout (%is)" % (time.time() - i.lastTx) + i.handle_close() for i in self.inboundConnections.values() + self.outboundConnections.values() + self.listeningSockets.values() + self.udpSockets.values(): + if i.state == "close": + i.handle_close() if not (i.accepting or i.connecting or i.connected): reaper.append(i) for i in reaper: diff --git a/src/network/socks4a.py b/src/network/socks4a.py index d6cf2ad8..978ede04 100644 --- a/src/network/socks4a.py +++ b/src/network/socks4a.py @@ -86,7 +86,8 @@ class Socks4aConnection(Socks4a): try: return Socks4a.state_pre_connect(self) except Socks4aError as e: - self.handle_close(e.message) + self.close_reason = e.message + self.set_state("close") class Socks4aResolver(Socks4a): diff --git a/src/network/socks5.py b/src/network/socks5.py index e57e7c6a..52050ec9 100644 --- a/src/network/socks5.py +++ b/src/network/socks5.py @@ -154,7 +154,8 @@ class Socks5Connection(Socks5): try: return Socks5.state_pre_connect(self) except Socks5Error as e: - self.handle_close(e.message) + self.close_reason = e.message + self.set_state("close") class Socks5Resolver(Socks5): diff --git a/src/network/tcp.py b/src/network/tcp.py index dd813ac6..ab282fb4 100644 --- a/src/network/tcp.py +++ b/src/network/tcp.py @@ -213,12 +213,12 @@ class TCPConnection(BMProto, TLSDispatcher): def handle_write(self): TLSDispatcher.handle_write(self) - def handle_close(self, reason=None): + def handle_close(self): if self.isOutbound and not self.fullyEstablished: knownnodes.decreaseRating(self.destination) - BMProto.handle_close(self, reason) if self.fullyEstablished: UISignalQueue.put(('updateNetworkStatusTab', (self.isOutbound, False, self.destination))) + BMProto.handle_close(self) class Socks5BMConnection(Socks5Connection, TCPConnection): From 7b470d4b6665da8d9cdbf7038611ba817adcacdc Mon Sep 17 00:00:00 2001 From: Peter Surda Date: Thu, 19 Oct 2017 09:11:34 +0200 Subject: [PATCH 255/407] handle shutdown in receivequeuethread - sometimes during shutdown, the receivequeuethread would get stuck --- src/network/advanceddispatcher.py | 8 +++++--- src/network/receivequeuethread.py | 2 +- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/src/network/advanceddispatcher.py b/src/network/advanceddispatcher.py index 2115c454..35121d1c 100644 --- a/src/network/advanceddispatcher.py +++ b/src/network/advanceddispatcher.py @@ -5,6 +5,7 @@ import time import asyncore_pollchoose as asyncore from debug import logger from helper_threading import BusyError, nonBlocking +import state class AdvancedDispatcher(asyncore.dispatcher): _buf_len = 2097152 # 2MB @@ -50,16 +51,17 @@ class AdvancedDispatcher(asyncore.dispatcher): del self.read_buf[0:length] def process(self): - if not self.connected: - return False - while True: + while self.connected and not state.shutdown: try: with nonBlocking(self.processingLock): + if not self.connected or state.shutdown: + break if len(self.read_buf) < self.expectBytes: return False if getattr(self, "state_" + str(self.state))() is False: break except AttributeError: + logger.error("Unknown state %s", self.state) raise except BusyError: return False diff --git a/src/network/receivequeuethread.py b/src/network/receivequeuethread.py index a617e16e..5399b972 100644 --- a/src/network/receivequeuethread.py +++ b/src/network/receivequeuethread.py @@ -30,7 +30,7 @@ class ReceiveQueueThread(threading.Thread, StoppableThread): except Queue.Empty: continue - if self._stopped: + if self._stopped or state.shutdown: break # cycle as long as there is data From ab458531e8db577941d5053d6bb0c700e50d60ed Mon Sep 17 00:00:00 2001 From: Peter Surda Date: Thu, 19 Oct 2017 09:16:29 +0200 Subject: [PATCH 256/407] Changes in SOCKS and onion handling in connectionchooser - onion addresses have a priority of 1 when SOCKS is on - don't connect to private/loopback addresses when SOCKS is on --- src/network/connectionchooser.py | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/src/network/connectionchooser.py b/src/network/connectionchooser.py index ee2a8b40..819dfeb1 100644 --- a/src/network/connectionchooser.py +++ b/src/network/connectionchooser.py @@ -3,6 +3,7 @@ import random from bmconfigparser import BMConfigParser import knownnodes +import protocol from queues import portCheckerQueue import state @@ -38,8 +39,15 @@ def chooseConnection(stream): except TypeError: print "Error in %s" % (peer) rating = 0 - if haveOnion and peer.host.endswith('.onion') and rating > 0: - rating *= 10 + if haveOnion: + # onion addresses have a higher priority when SOCKS + if peer.host.endswith('.onion') and rating > 0: + rating = 1 + else: + encodedAddr = protocol.encodeHost(peer.host) + # don't connect to local IPs when using SOCKS + if not protocol.checkIPAddress(encodedAddr, False): + continue if rating > 1: rating = 1 try: From 364642404a98a8d842b0554c76253a011a6b25bc Mon Sep 17 00:00:00 2001 From: PyBitmessage Translations Date: Thu, 19 Oct 2017 12:26:51 +0200 Subject: [PATCH 257/407] Auto-updated language ja from transifex --- src/translations/bitmessage_ja.qm | Bin 66995 -> 66960 bytes src/translations/bitmessage_ja.ts | 306 +++++++++++++++--------------- 2 files changed, 158 insertions(+), 148 deletions(-) diff --git a/src/translations/bitmessage_ja.qm b/src/translations/bitmessage_ja.qm index 4a639cbf8891e2567c3d6cf1a334c605391371df..3756d6d34e241eba949ddf23e8c87202d607df8c 100644 GIT binary patch delta 2987 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 diff --git a/src/translations/bitmessage_ja.ts b/src/translations/bitmessage_ja.ts index f62493ba..90e49dc2 100644 --- a/src/translations/bitmessage_ja.ts +++ b/src/translations/bitmessage_ja.ts @@ -58,12 +58,12 @@ EmailGatewayRegistrationDialog - + Registration failed: 登録に失敗しました: - + The requested email address is not available, please try a new one. Fill out the new desired email address (including @mailchuck.com) below: リクエストしたメールアドレスは利用できません。新しいメールアドレスをお試しください。 新しい希望メールアドレス (@mailchuck.com を含む) を次のように記入してください: @@ -283,7 +283,7 @@ Please type the desired email address (including @mailchuck.com) below: %1は古いバージョン1のアドレスです。バージョン1のアドレスはサポートが終了しています。すぐに削除しますか? - + Waiting for their encryption key. Will request it again soon. 暗号鍵を待っています。 すぐにもう一度リクエストします。 @@ -293,17 +293,17 @@ Please type the desired email address (including @mailchuck.com) below: - + Queued. キューに入りました。 - + Message sent. Waiting for acknowledgement. Sent at %1 メッセージを送信しました。 確認応答を待っています。 %1 で送信されました - + Message sent. Sent at %1 メッセージは送信されました。送信先: %1 @@ -313,47 +313,47 @@ Please type the desired email address (including @mailchuck.com) below: - + Acknowledgement of the message received %1 メッセージの確認を受け取りました %1 - + Broadcast queued. 配信がキューに入りました。 - + Broadcast on %1 配信: %1 - + Problem: The work demanded by the recipient is more difficult than you are willing to do. %1 問題: 受信者が要求している処理は現在あなたが設定しているよりも高い難易度です。 %1 - + Problem: The recipient's encryption key is no good. Could not encrypt message. %1 問題: 受信者の暗号鍵は正当でない物です。メッセージを暗号化できません。 %1 - + Forced difficulty override. Send should start soon. 難易度を強制上書きしました。まもなく送信されます。 - + Unknown status: %1 %2 不明なステータス: %1 %2 - + Not Connected 未接続 - + Show Bitmessage Bitmessageを表示 @@ -363,12 +363,12 @@ Please type the desired email address (including @mailchuck.com) below: 送る - + Subscribe 購読 - + Channel チャンネル @@ -378,66 +378,66 @@ Please type the desired email address (including @mailchuck.com) below: 終了 - + You may manage your keys by editing the keys.dat file stored in the same directory as this program. It is important that you back up this file. プログラムを同じディレクトリに保存されているkeys.datファイルを編集することで鍵を管理できます。ファイルをバックアップしておくことも重要です。 - + You may manage your keys by editing the keys.dat file stored in %1 It is important that you back up this file. %1に保存されているkeys.datファイルを編集することで鍵を管理できます。ファイルをバックアップしておくことも重要です。 - + Open keys.dat? keys.datを開きますか? - + You may manage your keys by editing the keys.dat file stored in the same directory as this program. It is important that you back up this file. Would you like to open the file now? (Be sure to close Bitmessage before making any changes.) プログラムを同じディレクトリに保存されているkeys.datファイルを編集することで鍵を管理できます。ファイルをバックアップしておくことも重要です。すぐにファイルを開きますか?(必ず編集する前にBitmessageを終了してください) - + You may manage your keys by editing the keys.dat file stored in %1 It is important that you back up this file. Would you like to open the file now? (Be sure to close Bitmessage before making any changes.) %1に保存されているkeys.datファイルを編集することで鍵を管理できます。ファイルをバックアップしておくことも重要です。すぐにファイルを開きますか?(必ず編集する前にBitmessageを終了してください) - + Delete trash? ゴミ箱を空にしますか? - + Are you sure you want to delete all trashed messages? ゴミ箱内のメッセージを全て削除してもよろしいですか? - + bad passphrase 不正なパスフレーズ - + You must type your passphrase. If you don't have one then this is not the form for you. パスフレーズを入力してください。パスフレーズがない場合は入力する必要はありません。 - + Bad address version number 不正なアドレスのバージョン番号 - + Your address version number must be a number: either 3 or 4. アドレスのバージョン番号は数字にする必要があります: 3 または 4。 - + Your address version number must be either 3 or 4. アドレスのバージョン番号は、3 または 4 のどちらかにする必要があります。 @@ -507,22 +507,22 @@ It is important that you back up this file. Would you like to open the file now? - + Connection lost 接続が切断されました - + Connected 接続済み - + Message trashed メッセージが削除されました - + The TTL, or Time-To-Live is the length of time that the network will hold the message. The recipient must get it during this time. If your Bitmessage client does not hear an acknowledgement, it will resend the message automatically. The longer the Time-To-Live, the @@ -533,17 +533,17 @@ It is important that you back up this file. Would you like to open the file now? コンピュータがメッセージを送信するために必要な処理が増えます。 多くの場合 4〜5 日のTTL(Time-To-Live)が適切です。 - + Message too long メッセージが長すぎます - + The message that you are trying to send is too long by %1 bytes. (The maximum is 261644 bytes). Please cut it down before sending. 送信しようとしているメッセージが %1 バイト長すぎます。 (最大は261644バイトです)。 送信する前に短くしてください。 - + Error: Your account wasn't registered at an email gateway. Sending registration now as %1, please wait for the registration to be processed before retrying sending. エラー: アカウントがメールゲートウェイに登録されていません。 今 %1 として登録を送信しています。送信を再試行する前に、登録が処理されるまでお待ちください。 @@ -588,57 +588,57 @@ It is important that you back up this file. Would you like to open the file now? - + Error: You must specify a From address. If you don't have one, go to the 'Your Identities' tab. エラー: 送信元アドレスを指定してください。まだ作成していない場合には「アドレス一覧」のタブを開いてください。 - + Address version number アドレスのバージョン番号 - + Concerning the address %1, Bitmessage cannot understand address version numbers of %2. Perhaps upgrade Bitmessage to the latest version. アドレス %1 に接続。%2 のバージョン番号は処理できません。Bitmessageを最新のバージョンへアップデートしてください。 - + Stream number ストリーム番号 - + Concerning the address %1, Bitmessage cannot handle stream numbers of %2. Perhaps upgrade Bitmessage to the latest version. アドレス %1 に接続。%2 のストリーム番号は処理できません。Bitmessageを最新のバージョンへアップデートしてください。 - + Warning: You are currently not connected. Bitmessage will do the work necessary to send the message but it won't send until you connect. 警告: 接続されていません。Bitmessageはメッセージの処理を行いますが、ネットワークに接続するまで送信はされません。 - + Message queued. メッセージがキューに入りました。 - + Your 'To' field is empty. 宛先が指定されていません。 - + Right click one or more entries in your address book and select 'Send message to this address'. アドレス帳から一つ、または複数のアドレスを右クリックして「このアドレスへ送信」を選んでください。 - + Fetched address from namecoin identity. namecoin IDからアドレスを取得。 - + New Message 新規メッセージ @@ -648,7 +648,7 @@ It is important that you back up this file. Would you like to open the file now? - + Sending email gateway registration request メールゲートウェイの登録リクエストを送信しています @@ -663,142 +663,142 @@ It is important that you back up this file. Would you like to open the file now? 入力されたアドレスは不正です。無視されました。 - + Error: You cannot add the same address to your address book twice. Try renaming the existing one if you want. エラー: 同じアドレスを複数アドレス帳に追加する事はできません。既存の項目をリネームしてください。 - + Error: You cannot add the same address to your subscriptions twice. Perhaps rename the existing one if you want. エラー: 購読に、同じアドレスを2回追加することはできません。 必要に応じて、既存の名前を変更してください。 - + Restart 再開 - + You must restart Bitmessage for the port number change to take effect. ポート番号の変更を有効にするにはBitmessageを再起動してください。 - + Bitmessage will use your proxy from now on but you may want to manually restart Bitmessage now to close existing connections (if any). プロキシの設定を有効にするには手動でBitmessageを再起動してください。既に接続がある場合は切断されます。 - + Number needed 数字が必要です - + Your maximum download and upload rate must be numbers. Ignoring what you typed. 最大ダウンロード数とアップロード数は数字にする必要があります。 入力されたものを無視します。 - + Will not resend ever 今後再送信されません - + Note that the time limit you entered is less than the amount of time Bitmessage waits for the first resend attempt therefore your messages will never be resent. 入力した時間制限は、Bitmessageが最初の再送試行を待つ時間よりも短いため、メッセージは再送信されないことにご注意ください。 - + Sending email gateway unregistration request メールゲートウェイの登録抹消リクエストを送信しています - + Sending email gateway status request メールゲートウェイの状態リクエストを送信しています - + Passphrase mismatch パスフレーズが一致しません - + The passphrase you entered twice doesn't match. Try again. 再度入力されたパスフレーズが一致しません。再入力してください。 - + Choose a passphrase パスフレーズを選択してください - + You really do need a passphrase. パスフレーズが必要です。 - + Address is gone アドレスが無効になりました - + Bitmessage cannot find your address %1. Perhaps you removed it? アドレス %1 が見つかりません。既に削除していませんか? - + Address disabled アドレスが無効になりました - + Error: The address from which you are trying to send is disabled. You'll have to enable it on the 'Your Identities' tab before using it. エラー: 送信しようとしたアドレスは無効になっています。使用する前に「アドレス一覧」で有効にしてください。 - + Entry added to the Address Book. Edit the label to your liking. アドレス帳に項目が追加されました。ラベルは自由に編集できます。 - + Entry added to the blacklist. Edit the label to your liking. ブラックリストに項目が追加されました。ラベルは自由に編集できます。 - + Error: You cannot add the same address to your blacklist twice. Try renaming the existing one if you want. エラー: ブラックリストに同じアドレスを2回追加することはできません。 必要に応じて既存の名前を変更してみてください。 - + Moved items to trash. アイテムをゴミ箱へ移動。 - + Undeleted item. アイテムの削除を元に戻します。 - + Save As... 形式を選択して保存 - + Write error. 書き込みエラー。 - + No addresses selected. アドレスが未選択です。 - + If you delete the subscription, messages that you already received will become inaccessible. Maybe you can consider disabling the subscription instead. Disabled subscriptions will not receive new messages, but you can still view messages you already received. Are you sure you want to delete the subscription? @@ -807,7 +807,7 @@ Are you sure you want to delete the subscription? 購読を削除してもよろしいですか? - + If you delete the channel, messages that you already received will become inaccessible. Maybe you can consider disabling the channel instead. Disabled channels will not receive new messages, but you can still view messages you already received. Are you sure you want to delete the channel? @@ -816,92 +816,92 @@ Are you sure you want to delete the channel? チャンネルを削除してもよろしいですか? - + Do you really want to remove this avatar? このアバターを削除してもよろしいですか? - + You have already set an avatar for this address. Do you really want to overwrite it? すでにこのアドレスのアバターを設定しています。 上書きしてもよろしいですか? - + Start-on-login not yet supported on your OS. ログイン時に開始は、まだお使いのOSでサポートされていません。 - + Minimize-to-tray not yet supported on your OS. トレイに最小化は、まだお使いのOSでサポートされていません。 - + Tray notifications not yet supported on your OS. トレイ通知は、まだお使いのOSでサポートされていません。 - + Testing... テスト中 - + This is a chan address. You cannot use it as a pseudo-mailing list. chanアドレスは仮想メーリングリストのアドレスには使用できません。 - + The address should start with ''BM-'' アドレスは「BM-」から始まります - + The address is not typed or copied correctly (the checksum failed). このアドレスは正しく入力、またはコピーされていません。(チェックサムが一致しません)。 - + The version number of this address is higher than this software can support. Please upgrade Bitmessage. このアドレスのバージョン番号はこのプログラムのサポート範囲外です。Bitmessageをアップデートしてください。 - + The address contains invalid characters. 入力されたアドレスは不正な文字を含んでいます。 - + Some data encoded in the address is too short. このアドレスでエンコードされたデータが短すぎます。 - + Some data encoded in the address is too long. このアドレスでエンコードされたデータが長過ぎます。 - + Some data encoded in the address is malformed. このアドレスでエンコードされた一部のデータが不正です。 - + Enter an address above. 上にアドレスを入力してください。 - + Address is an old type. We cannot display its past broadcasts. アドレスが古い形式です。 過去の配信は表示できません。 - + There are no recent broadcasts from this address to display. このアドレスから表示する最近の配信はありません。 - + You are using TCP port %1. (This can be changed in the settings). 使用中のポート %1 (設定で変更できます)。 @@ -1111,47 +1111,47 @@ Are you sure you want to delete the channel? 新しい項目を追加 - + Display the %1 recent broadcast(s) from this address. このアドレスから%1の最新の配信を表示します。 - + New version of PyBitmessage is available: %1. Download it from https://github.com/Bitmessage/PyBitmessage/releases/latest 新しいバージョンの PyBitmessage が利用可能です: %1。 https://github.com/Bitmessage/PyBitmessage/releases/latest からダウンロードしてください - + Waiting for PoW to finish... %1% PoW(証明)が完了するのを待っています... %1% - + Shutting down Pybitmessage... %1% Pybitmessageをシャットダウンしています... %1% - + Waiting for objects to be sent... %1% オブジェクトの送信待ち... %1% - + Saving settings... %1% 設定を保存しています... %1% - + Shutting down core... %1% コアをシャットダウンしています... %1% - + Stopping notifications... %1% 通知を停止しています... %1% - + Shutdown imminent... %1% すぐにシャットダウンします... %1% @@ -1166,12 +1166,12 @@ Are you sure you want to delete the channel? %n 日 - + Shutting down PyBitmessage... %1% PyBitmessageをシャットダウンしています... %1% - + Sent 送信済 @@ -1275,7 +1275,7 @@ Receiver's required difficulty: %1 and %2 問題: あなた自身またはチャンネルにメッセージを送信しようとしていますが、暗号鍵がkeys.datファイルに見つかりませんでした。 メッセージを暗号化できませんでした。 %1 - + Doing work necessary to send message. メッセージの送信に必要な処理を行っています。 @@ -1285,7 +1285,7 @@ Receiver's required difficulty: %1 and %2 メッセージを送信しました。 確認応答を待っています。 %1 で送信しました - + Doing work necessary to request encryption key. 暗号鍵のリクエストに必要な処理を行っています。 @@ -1315,32 +1315,32 @@ Receiver's required difficulty: %1 and %2 すべてのメッセージを既読にする - + Are you sure you would like to mark all messages read? すべてのメッセージを既読にしてもよろしいですか? - + Doing work necessary to send broadcast. 配信に必要な処理を行っています。 - + Proof of work pending PoW(証明)を待っています - + %n object(s) pending proof of work %n オブジェクトが証明待ち (PoW) - + %n object(s) waiting to be distributed %n オブジェクトが配布待ち - + Wait until these tasks finish? これらのタスクが完了するまで待ちますか? @@ -1405,7 +1405,7 @@ Receiver's required difficulty: %1 and %2 GPUが正しく求められないため、OpenCLが無効になりました。 開発者に報告してください。 - + Set notification sound... 通知音を設定... @@ -1430,92 +1430,102 @@ Receiver's required difficulty: %1 and %2 チャンネルにはお勧めしません - + + Quiet Mode + マナーモード + + + Problems connecting? Try enabling UPnP in the Network Settings 接続に問題がありますか? ネットワーク設定でUPnPを有効にしてみてください - + You are trying to send an email instead of a bitmessage. This requires registering with a gateway. Attempt to register? Bitmessage の代わりにメールを送信しようとしています。 これは、ゲートウェイに登録する必要があります。 登録しますか? - + Error: Bitmessage addresses start with BM- Please check the recipient address %1 エラー: BitmessageのアドレスはBM-で始まります。 受信者のアドレス %1 を確認してください - + Error: The recipient address %1 is not typed or copied correctly. Please check it. エラー: 受信者のアドレス %1 は正しく入力、またはコピーされていません。確認して下さい。 - + Error: The recipient address %1 contains invalid characters. Please check it. エラー: 受信者のアドレス %1 は不正な文字を含んでいます。確認して下さい。 - + Error: The version of the recipient address %1 is too high. Either you need to upgrade your Bitmessage software or your acquaintance is being clever. エラー: 受信者アドレスのバージョン %1 は高すぎます。 Bitmessageソフトウェアをアップグレードする必要があるか、連絡先が賢明になっているかのいずれかです。 - + Error: Some data encoded in the recipient address %1 is too short. There might be something wrong with the software of your acquaintance. エラー: アドレス %1 でエンコードされたデータが短すぎます。連絡先のソフトウェアが何かしら誤っている可能性があります。 - + Error: Some data encoded in the recipient address %1 is too long. There might be something wrong with the software of your acquaintance. エラー: 受信者のアドレス %1 でエンコードされたデータが短すぎます。連絡先のソフトウェアが何かしら誤っている可能性があります。 - + Error: Some data encoded in the recipient address %1 is malformed. There might be something wrong with the software of your acquaintance. エラー: 受信者のアドレス %1 でエンコードされたデータの一部が不正です。連絡先のソフトウェアが何かしら誤っている可能性があります。 - + Error: Something is wrong with the recipient address %1. エラー: 受信者のアドレス %1 には何かしら誤りがあります。 - + + Error: %1 + + + + From %1 送信元 %1 - + Synchronisation pending 同期を保留しています - + Bitmessage hasn't synchronised with the network, %n object(s) to be downloaded. If you quit now, it may cause delivery delays. Wait until the synchronisation finishes? Bitmessageはネットワークと同期していません。%n のオブジェクトをダウンロードする必要があります。 今、終了すると、配送が遅れることがあります。 同期が完了するまで待ちますか? - + Not connected 未接続 - + Bitmessage isn't connected to the network. If you quit now, it may cause delivery delays. Wait until connected and the synchronisation finishes? Bitmessageはネットワークに接続していません。 今、終了すると、配送が遅れることがあります。 接続して、同期が完了するまで待ちますか? - + Waiting for network connection... ネットワーク接続を待っています... - + Waiting for finishing synchronisation... 同期の完了を待っています... - + You have already set a notification sound for this address book entry. Do you really want to overwrite it? すでにこのアドレス帳エントリの通知音を設定しています。 上書きしてもよろしいですか? @@ -1901,27 +1911,27 @@ The 'Random Number' option is selected by default but deterministic ad - + Since startup on %1 起動日時 %1 - + Down: %1/s Total: %2 ダウン: %1/秒 合計: %2 - + Up: %1/s Total: %2 アップ: %1/秒 合計: %2 - + Total Connections: %1 接続数: %1 - + Inventory lookups per second: %1 毎秒のインベントリ検索: %1 @@ -1941,27 +1951,27 @@ The 'Random Number' option is selected by default but deterministic ad ネットワークの状態 - + byte(s) バイト - + Object(s) to be synced: %n 同期する必要のあるオブジェクト: %n - + Processed %n person-to-person message(s). %n 通の1対1のメッセージを処理しました。 - + Processed %n broadcast message(s). %n 件の配信を処理しました。 - + Processed %n public key(s). %n 件の公開鍵を処理しました。 @@ -2065,8 +2075,8 @@ The 'Random Number' option is selected by default but deterministic ad - Chan passhphrase/name: - チャンネルのパスフレーズ/名前: + Chan passphrase/name: + @@ -2087,17 +2097,17 @@ The 'Random Number' option is selected by default but deterministic ad newchandialog - + Successfully created / joined chan %1 チャンネル %1 を正常に作成 / 参加しました - + Chan creation / joining failed チャンネルの作成 / 参加に失敗しました - + Chan creation / joining cancelled チャンネルの作成 / 参加をキャンセルしました From 9be36f2d78f6ae46562a393c2fd9097a5bb08f3a Mon Sep 17 00:00:00 2001 From: Dmitri Bogomolov <4glitch@gmail.com> Date: Tue, 17 Oct 2017 01:30:01 +0300 Subject: [PATCH 258/407] Use unicode instead of str for MessagingMenu --- src/plugins/indicator_libmessaging.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/plugins/indicator_libmessaging.py b/src/plugins/indicator_libmessaging.py index 96ab1516..36178663 100644 --- a/src/plugins/indicator_libmessaging.py +++ b/src/plugins/indicator_libmessaging.py @@ -1,7 +1,7 @@ # -*- coding: utf-8 -*- import gi -gi.require_version('MessagingMenu', '1.0') +gi.require_version('MessagingMenu', '1.0') # noqa:E402 from gi.repository import MessagingMenu from pybitmessage.bitmessageqt.utils import str_broadcast_subscribers @@ -19,9 +19,9 @@ class IndicatorLibmessaging(object): return self._menu = { - 'send': str(_translate('MainWindow', 'Send')), - 'messages': str(_translate('MainWindow', 'Messages')), - 'subscriptions': str(_translate('MainWindow', 'Subscriptions')) + 'send': unicode(_translate('MainWindow', 'Send')), + 'messages': unicode(_translate('MainWindow', 'Messages')), + 'subscriptions': unicode(_translate('MainWindow', 'Subscriptions')) } self.new_message_item = self.new_broadcast_item = None From 8fcdf51e576e899f3a63abad9c15ef435a72640a Mon Sep 17 00:00:00 2001 From: Dmitri Bogomolov <4glitch@gmail.com> Date: Wed, 18 Oct 2017 22:03:06 +0300 Subject: [PATCH 259/407] One more unicode related exception - when editing a contact label --- src/bitmessageqt/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/bitmessageqt/__init__.py b/src/bitmessageqt/__init__.py index 4d2c0bad..4cda426d 100644 --- a/src/bitmessageqt/__init__.py +++ b/src/bitmessageqt/__init__.py @@ -3885,7 +3885,7 @@ class MyForm(settingsmixin.SMainWindow): self.rerenderMessagelistToLabels() completerList = self.ui.lineEditTo.completer().model().stringList() for i in range(len(completerList)): - if str(completerList[i]).endswith(" <" + item.address + ">"): + if unicode(completerList[i]).endswith(" <" + item.address + ">"): completerList[i] = item.label + " <" + item.address + ">" self.ui.lineEditTo.completer().model().setStringList(completerList) From 393769307d0ffbd1e02bfd2893927cba0e20c191 Mon Sep 17 00:00:00 2001 From: Dmitri Bogomolov <4glitch@gmail.com> Date: Thu, 19 Oct 2017 21:17:30 +0300 Subject: [PATCH 260/407] Slightly redrawn the scalable icon --- desktop/can-icon.svg | 176 +++++++++++++++++-------------------------- 1 file changed, 71 insertions(+), 105 deletions(-) diff --git a/desktop/can-icon.svg b/desktop/can-icon.svg index b4c2bd89..7c854e34 100644 --- a/desktop/can-icon.svg +++ b/desktop/can-icon.svg @@ -9,11 +9,11 @@ xmlns="http://www.w3.org/2000/svg" xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" - width="744.09448819" - height="1052.3622047" + width="793.70081" + height="1122.5197" id="svg2" version="1.1" - inkscape:version="0.48.4 r9939" + inkscape:version="0.92.1 r" sodipodi:docname="can-icon.svg"> @@ -45,7 +45,7 @@ image/svg+xml - + @@ -55,129 +55,95 @@ id="layer1"> - - - - - - - - - - - + transform="translate(10.559462,156.88343)"> - + sodipodi:nodetypes="ccccc" + inkscape:export-xdpi="4.57552" + inkscape:export-ydpi="4.57552" /> + d="M 395.54691,28.063323 112.5256,508.60245" + style="fill:#808080;stroke:#000000;stroke-width:1.64679658px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:0.11949684" + inkscape:export-xdpi="4.57552" + inkscape:export-ydpi="4.57552" /> - + d="M 193.26809,521.672 466.89638,43.16174" + style="fill:none;stroke:#000000;stroke-width:1.65778315px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:0.06918239" + inkscape:export-xdpi="4.57552" + inkscape:export-ydpi="4.57552" /> - - + d="M 283.66518,559.54595 549.75376,77.722668" + style="fill:#b3b3b3;stroke:#000000;stroke-width:1.65072334px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:0.07547171" + inkscape:export-xdpi="4.57552" + inkscape:export-ydpi="4.57552" /> + d="M 442.34039,696.99151 701.70079,210.05539" + style="fill:none;stroke:#000000;stroke-width:1.65072334px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:0.21383649" + inkscape:export-xdpi="4.57552" + inkscape:export-ydpi="4.57552" /> + inkscape:export-xdpi="4.57552" + inkscape:export-ydpi="4.57552" /> + inkscape:export-xdpi="4.57552" + inkscape:export-ydpi="4.57552" /> + inkscape:export-xdpi="4.57552" + inkscape:export-ydpi="4.57552" /> + inkscape:export-xdpi="4.57552" + inkscape:export-ydpi="4.57552" /> + + + From 15857e6551862a119c699a68524ab2f9eed1ca99 Mon Sep 17 00:00:00 2001 From: Peter Surda Date: Fri, 20 Oct 2017 01:07:30 +0200 Subject: [PATCH 261/407] Asyncore updates - reduce buffer size to 128kB (was 2MB) - IP address handling use str instead of buffer (the latter, even though it should be faster, breaks the code on Windows) - read up to full buffer after fully established (otherwise downloads become too slow due to the loop time). This reverts a change made in d28a7bfb862e6d9b9716098b27e5ec1d2f482da8 --- src/network/advanceddispatcher.py | 4 ++-- src/network/bmproto.py | 8 ++++---- src/network/udp.py | 2 +- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/network/advanceddispatcher.py b/src/network/advanceddispatcher.py index 35121d1c..50ddf44b 100644 --- a/src/network/advanceddispatcher.py +++ b/src/network/advanceddispatcher.py @@ -8,7 +8,7 @@ from helper_threading import BusyError, nonBlocking import state class AdvancedDispatcher(asyncore.dispatcher): - _buf_len = 2097152 # 2MB + _buf_len = 131072 # 128kB def __init__(self, sock=None): if not hasattr(self, '_map'): @@ -85,7 +85,7 @@ class AdvancedDispatcher(asyncore.dispatcher): if asyncore.maxDownloadRate > 0: self.downloadChunk = asyncore.downloadBucket try: - if self.expectBytes > 0: + if self.expectBytes > 0 and not self.fullyEstablished: self.downloadChunk = min(self.downloadChunk, self.expectBytes - len(self.read_buf)) if self.downloadChunk < 0: self.downloadChunk = 0 diff --git a/src/network/bmproto.py b/src/network/bmproto.py index d66d8c4b..445af9a9 100644 --- a/src/network/bmproto.py +++ b/src/network/bmproto.py @@ -144,16 +144,16 @@ class BMProto(AdvancedDispatcher, ObjectTracker): def decode_payload_node(self): services, host, port = self.decode_payload_content("Q16sH") if host[0:12] == '\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xFF\xFF': - host = socket.inet_ntop(socket.AF_INET, buffer(host, 12, 4)) + host = socket.inet_ntop(socket.AF_INET, str(host[12:16])) elif host[0:6] == '\xfd\x87\xd8\x7e\xeb\x43': # Onion, based on BMD/bitcoind host = base64.b32encode(host[6:]).lower() + ".onion" else: - host = socket.inet_ntop(socket.AF_INET6, buffer(host)) + host = socket.inet_ntop(socket.AF_INET6, str(host)) if host == "": # This can happen on Windows systems which are not 64-bit compatible # so let us drop the IPv6 address. - host = socket.inet_ntop(socket.AF_INET, buffer(host, 12, 4)) + host = socket.inet_ntop(socket.AF_INET, str(host[12:16])) return Node(services, host, port) @@ -376,7 +376,7 @@ class BMProto(AdvancedDispatcher, ObjectTracker): addresses = self._decode_addr() for i in addresses: seenTime, stream, services, ip, port = i - decodedIP = protocol.checkIPAddress(buffer(ip)) + decodedIP = protocol.checkIPAddress(str(ip)) if stream not in state.streamsInWhichIAmParticipating: continue if decodedIP is not False and seenTime > time.time() - BMProto.addressAlive: diff --git a/src/network/udp.py b/src/network/udp.py index 3d238a5e..e7f6974d 100644 --- a/src/network/udp.py +++ b/src/network/udp.py @@ -89,7 +89,7 @@ class UDPSocket(BMProto): remoteport = False for i in addresses: seenTime, stream, services, ip, port = i - decodedIP = protocol.checkIPAddress(buffer(ip)) + decodedIP = protocol.checkIPAddress(str(ip)) if stream not in state.streamsInWhichIAmParticipating: continue if seenTime < time.time() - BMProto.maxTimeOffset or seenTime > time.time() + BMProto.maxTimeOffset: From 2d34e7364899401bbb63668670ce83bbeee621d4 Mon Sep 17 00:00:00 2001 From: Peter Surda Date: Fri, 20 Oct 2017 01:21:49 +0200 Subject: [PATCH 262/407] Dandelion updates - fixes and feedback from @gfanti and @amiller - addresses #1049 - minor refactoring - two global child stems with fixed mapping between parent and child stem - allow child stems which don't support dandelion - only allow outbound connections to be stems - adjust stems if opening/closing outbound connections (should allow partial dandelion functionality when not enough outbound connections are available instead of breaking) --- src/bitmessagemain.py | 4 +- src/class_singleCleaner.py | 6 +- src/network/bmobject.py | 4 +- src/network/bmproto.py | 12 ++-- src/network/connectionpool.py | 18 +----- src/network/dandelion.py | 102 ++++++++++++++++++++++++++++------ src/network/invthread.py | 30 +++++----- src/network/objectracker.py | 6 +- src/network/tcp.py | 8 ++- 9 files changed, 122 insertions(+), 68 deletions(-) diff --git a/src/bitmessagemain.py b/src/bitmessagemain.py index 06d89ba8..83a41919 100755 --- a/src/bitmessagemain.py +++ b/src/bitmessagemain.py @@ -54,7 +54,7 @@ from bmconfigparser import BMConfigParser from inventory import Inventory from network.connectionpool import BMConnectionPool -from network.dandelion import DandelionStems +from network.dandelion import Dandelion from network.networkthread import BMNetworkThread from network.receivequeuethread import ReceiveQueueThread from network.announcethread import AnnounceThread @@ -251,7 +251,7 @@ class Main: sqlLookup.start() Inventory() # init - DandelionStems() # init, needs to be early because other thread may access it early + Dandelion() # init, needs to be early because other thread may access it early # SMTP delivery thread if daemon and BMConfigParser().safeGet("bitmessagesettings", "smtpdeliver", '') != '': diff --git a/src/class_singleCleaner.py b/src/class_singleCleaner.py index 3068910d..f3125806 100644 --- a/src/class_singleCleaner.py +++ b/src/class_singleCleaner.py @@ -10,7 +10,7 @@ from helper_sql import * from helper_threading import * from inventory import Inventory from network.connectionpool import BMConnectionPool -from network.dandelion import DandelionStems +from network.dandelion import Dandelion from debug import logger import knownnodes import queues @@ -136,9 +136,7 @@ class singleCleaner(threading.Thread, StoppableThread): for connection in BMConnectionPool().inboundConnections.values() + BMConnectionPool().outboundConnections.values(): connection.clean() # dandelion fluff trigger by expiration - for h, t in DandelionStems().timeouts: - if time.time() > t: - DandelionStems().remove(h) + Dandelion().expire() # discovery tracking exp = time.time() - singleCleaner.expireDiscoveredPeers diff --git a/src/network/bmobject.py b/src/network/bmobject.py index 4cde0c4f..f4c883ca 100644 --- a/src/network/bmobject.py +++ b/src/network/bmobject.py @@ -4,7 +4,7 @@ import time from addresses import calculateInventoryHash from debug import logger from inventory import Inventory -from network.dandelion import DandelionStems +from network.dandelion import Dandelion import protocol import state @@ -68,7 +68,7 @@ class BMObject(object): def checkAlreadyHave(self): # if it's a stem duplicate, pretend we don't have it - if self.inventoryHash in DandelionStems().stem: + if self.inventoryHash in Dandelion().hashMap: return if self.inventoryHash in Inventory(): raise BMObjectAlreadyHaveError() diff --git a/src/network/bmproto.py b/src/network/bmproto.py index 445af9a9..2469d6e4 100644 --- a/src/network/bmproto.py +++ b/src/network/bmproto.py @@ -10,7 +10,7 @@ from debug import logger from inventory import Inventory import knownnodes from network.advanceddispatcher import AdvancedDispatcher -from network.dandelion import DandelionStems, REASSIGN_INTERVAL +from network.dandelion import Dandelion from network.bmobject import BMObject, BMObjectInsufficientPOWError, BMObjectInvalidDataError, \ BMObjectExpiredError, BMObjectUnwantedStreamError, BMObjectInvalidError, BMObjectAlreadyHaveError import network.connectionpool @@ -279,8 +279,8 @@ class BMProto(AdvancedDispatcher, ObjectTracker): #TODO make this more asynchronous random.shuffle(items) for i in map(str, items): - if i in DandelionStems().stem and \ - self != DandelionStems().stem[i]: + if i in Dandelion().hashMap and \ + self != Dandelion().hashMap[i]: self.antiIntersectionDelay() logger.info('%s asked for a stem object we didn\'t offer to it.', self.destination) break @@ -325,12 +325,8 @@ class BMProto(AdvancedDispatcher, ObjectTracker): if BMConfigParser().safeGetBoolean("network", "dandelion") == 0: return True - if self.dandelionRefresh < time.time(): - self.dandelionRoutes = BMConnectionPool.dandelionRouteSelector(self) - self.dandelionRefresh = time.time() + REASSIGN_INTERVAL - for i in map(str, items): - DandelionStems().add(i, self, self.dandelionRoutes) + Dandelion().addHash(i, self) self.handleReceivedInventory(i) return True diff --git a/src/network/connectionpool.py b/src/network/connectionpool.py index 1e50eb1c..04336b00 100644 --- a/src/network/connectionpool.py +++ b/src/network/connectionpool.py @@ -9,6 +9,7 @@ from debug import logger import helper_bootstrap from network.proxy import Proxy import network.bmproto +from network.dandelion import Dandelion import network.tcp import network.udp from network.connectionchooser import chooseConnection @@ -51,23 +52,10 @@ class BMConnectionPool(object): except KeyError: pass - def dandelionRouteSelector(self, node): + def reRandomiseDandelionStems(self): # Choose 2 peers randomly # TODO: handle streams - peers = [] - connections = self.outboundConnections.values() - random.shuffle(connections) - for i in connections: - if i == node: - continue - try: - if i.services | protocol.NODE_DANDELION: - peers.append(i) - if len(peers) == 2: - break - except AttributeError: - continue - return peers + Dandelion().reRandomiseStems(self.outboundConnections.values()) def connectToStream(self, streamNumber): self.streams.append(streamNumber) diff --git a/src/network/dandelion.py b/src/network/dandelion.py index 840cc909..a7ef4083 100644 --- a/src/network/dandelion.py +++ b/src/network/dandelion.py @@ -1,4 +1,4 @@ -from random import choice +from random import choice, shuffle from threading import RLock from time import time @@ -8,31 +8,101 @@ from singleton import Singleton # randomise routes after 600 seconds REASSIGN_INTERVAL = 600 FLUFF_TRIGGER_TIMEOUT = 300 +MAX_STEMS = 2 @Singleton -class DandelionStems(): +class Dandelion(): def __init__(self): - self.stem = {} - self.source = {} - self.timeouts = {} + self.stem = [] + self.nodeMap = {} + self.hashMap = {} + self.timeout = {} + self.refresh = time() + REASSIGN_INTERVAL self.lock = RLock() - def add(self, hashId, source, stems): + def addHash(self, hashId, source): if BMConfigParser().safeGetInt('network', 'dandelion') == 0: return with self.lock: - try: - self.stem[hashId] = choice(stems) - except IndexError: - self.stem = None - self.source[hashId] = source - self.timeouts[hashId] = time() + self.hashMap[hashId] = self.getNodeStem(source) + self.timeout[hashId] = time() + FLUFF_TRIGGER_TIMEOUT - def remove(self, hashId): + def removeHash(self, hashId): with self.lock: try: - del self.stem[hashId] - del self.source[hashId] - del self.timeouts[hashId] + del self.hashMap[hashId] except KeyError: pass + try: + del self.timeout[hashId] + except KeyError: + pass + + def maybeAddStem(self, connection): + # fewer than MAX_STEMS outbound connections at last reshuffle? + with self.lock: + if len(self.stem) < MAX_STEMS: + self.stem.append(connection) + # active mappings pointing nowhere + for k in (k for k, v in self.nodeMap.iteritems() if self.nodeMap[k] is None): + self.nodeMap[k] = connection + for k in (k for k, v in self.hashMap.iteritems() if self.hashMap[k] is None): + self.hashMap[k] = connection + + def maybeRemoveStem(self, connection): + # is the stem active? + with self.lock: + if connection in self.stem: + self.stem.remove(connection) + # active mappings to pointing to the removed node + for k in (k for k, v in self.nodeMap.iteritems() if self.nodeMap[k] == connection): + self.nodeMap[k] = None + for k in (k for k, v in self.hashMap.iteritems() if self.hashMap[k] == connection): + self.hashMap[k] = None + if len(self.stem) < MAX_STEMS: + self.stem.append(connection) + + def pickStem(self, parent=None): + try: + # pick a random from available stems + stem = choice(range(len(self.stem))) + if self.stem[stem] == parent: + # one stem available and it's the parent + if len(self.stem) == 1: + return None + # else, pick the other one + return self.stem[1 - stem] + # all ok + return self.stem[stem] + except IndexError: + # no stems available + return None + + def getNodeStem(self, node=None): + with self.lock: + try: + return self.nodeMap[node] + except KeyError: + self.nodeMap[node] = self.pickStem() + return self.nodeMap[node] + + def getHashStem(self, hashId): + with self.lock: + return self.hashMap[hashId] + + def expire(self): + with self.lock: + deadline = time() + toDelete = [k for k, v in self.hashMap.iteritems() if self.timeout[k] < deadline] + for k in toDelete: + del self.timeout[k] + del self.hashMap[k] + + def reRandomiseStems(self, connections): + shuffle(connections) + with self.lock: + # random two connections + self.stem = connections[:MAX_STEMS] + self.nodeMap = {} + # hashMap stays to cater for pending stems + self.refresh = time() + REASSIGN_INTERVAL diff --git a/src/network/invthread.py b/src/network/invthread.py index 4f26c0fa..5852df0b 100644 --- a/src/network/invthread.py +++ b/src/network/invthread.py @@ -7,7 +7,7 @@ import addresses from bmconfigparser import BMConfigParser from helper_threading import StoppableThread from network.connectionpool import BMConnectionPool -from network.dandelion import DandelionStems, REASSIGN_INTERVAL +from network.dandelion import Dandelion from queues import invQueue import protocol import state @@ -17,26 +17,17 @@ class InvThread(threading.Thread, StoppableThread): threading.Thread.__init__(self, name="InvBroadcaster") self.initStop() self.name = "InvBroadcaster" - # for locally generated objects - self.dandelionRoutes = [] - self.dandelionRefresh = 0 - - def dandelionLocalRouteRefresh(self): - if self.dandelionRefresh < time(): - self.dandelionRoutes = BMConnectionPool().dandelionRouteSelector(None) - self.dandelionRefresh = time() + REASSIGN_INTERVAL def run(self): while not state.shutdown: chunk = [] while True: - self.dandelionLocalRouteRefresh() try: data = invQueue.get(False) chunk.append((data[0], data[1])) # locally generated if len(data) == 2: - DandelionStems().add(data[1], None, self.dandelionRoutes) + Dandelion().addHash(data[1], None) BMConnectionPool().handleReceivedObject(data[0], data[1]) # came over the network else: @@ -61,17 +52,19 @@ class InvThread(threading.Thread, StoppableThread): del connection.objectsNewToThem[inv[1]] except KeyError: continue - if inv[1] in DandelionStems().stem: - if connection == DandelionStems().stem[inv[1]]: + try: + if connection == Dandelion().hashMap[inv[1]]: # Fluff trigger by RNG # auto-ignore if config set to 0, i.e. dandelion is off - if randint(1, 100) < BMConfigParser().safeGetBoolean("network", "dandelion"): + # send a normal inv if stem node doesn't support dandelion + if randint(1, 100) < BMConfigParser().safeGetBoolean("network", "dandelion") and \ + connection.services | protocol.NODE_DANDELION > 0: stems.append(inv[1]) else: fluffs.append(inv[1]) - continue - else: + except KeyError: fluffs.append(inv[1]) + if fluffs: shuffle(fluffs) connection.append_write_buf(protocol.CreatePacket('inv', \ @@ -80,7 +73,12 @@ class InvThread(threading.Thread, StoppableThread): shuffle(stems) connection.append_write_buf(protocol.CreatePacket('dinv', \ addresses.encodeVarint(len(stems)) + "".join(stems))) + invQueue.iterate() for i in range(len(chunk)): invQueue.task_done() + + if Dandelion().refresh < time(): + BMConnectionPool().reRandomiseDandelionStems() + self.stop.wait(1) diff --git a/src/network/objectracker.py b/src/network/objectracker.py index 7149f4b1..62016d75 100644 --- a/src/network/objectracker.py +++ b/src/network/objectracker.py @@ -4,7 +4,7 @@ from threading import RLock from debug import logger from inventory import Inventory -from network.dandelion import DandelionStems +from network.dandelion import Dandelion haveBloom = False @@ -84,9 +84,9 @@ class ObjectTracker(object): if hashId not in Inventory(): with self.objectsNewToMeLock: self.objectsNewToMe[hashId] = True - elif hashId in DandelionStems().stem: + elif hashId in Dandelion().hashMap: # Fluff trigger by cycle detection - DandelionStems().remove(hashId) + Dandelion().removeHash(hashId) with self.objectsNewToMeLock: self.objectsNewToMe[hashId] = True diff --git a/src/network/tcp.py b/src/network/tcp.py index ab282fb4..70e22e08 100644 --- a/src/network/tcp.py +++ b/src/network/tcp.py @@ -18,7 +18,7 @@ from network.advanceddispatcher import AdvancedDispatcher from network.bmproto import BMProtoError, BMProtoInsufficientDataError, BMProtoExcessiveDataError, BMProto from network.bmobject import BMObject, BMObjectInsufficientPOWError, BMObjectInvalidDataError, BMObjectExpiredError, BMObjectUnwantedStreamError, BMObjectInvalidError, BMObjectAlreadyHaveError import network.connectionpool -from network.dandelion import DandelionStems +from network.dandelion import Dandelion from network.node import Node import network.asyncore_pollchoose as asyncore from network.proxy import Proxy, ProxyError, GeneralProxyError @@ -106,6 +106,8 @@ class TCPConnection(BMProto, TLSDispatcher): self.fullyEstablished = True if self.isOutbound: knownnodes.increaseRating(self.destination) + if self.isOutbound: + Dandelion().maybeAddStem(self) self.sendAddr() self.sendBigInv() @@ -166,7 +168,7 @@ class TCPConnection(BMProto, TLSDispatcher): with self.objectsNewToThemLock: for objHash in Inventory().unexpired_hashes_by_stream(stream): # don't advertise stem objects on bigInv - if objHash in DandelionStems().stem: + if objHash in Dandelion().hashMap: continue bigInvList[objHash] = 0 self.objectsNewToThem[objHash] = time.time() @@ -218,6 +220,8 @@ class TCPConnection(BMProto, TLSDispatcher): knownnodes.decreaseRating(self.destination) if self.fullyEstablished: UISignalQueue.put(('updateNetworkStatusTab', (self.isOutbound, False, self.destination))) + if self.isOutbound: + Dandelion().maybeRemoveStem(self) BMProto.handle_close(self) From a746ba9da7ea51ab6fd308ee502d53307720efee Mon Sep 17 00:00:00 2001 From: Peter Surda Date: Fri, 20 Oct 2017 13:21:39 +0200 Subject: [PATCH 263/407] Stop downloading objects with insufficient PoW - object with insufficient PoW weren't correctly detected and it tried to download them over and over again --- src/network/bmproto.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/network/bmproto.py b/src/network/bmproto.py index 2469d6e4..a3f7d620 100644 --- a/src/network/bmproto.py +++ b/src/network/bmproto.py @@ -340,11 +340,11 @@ class BMProto(AdvancedDispatcher, ObjectTracker): logger.info('The payload length of this object is too large (%s bytes). Ignoring it.' % len(self.payload) - self.payloadOffset) raise BMProtoExcessiveDataError() - self.object.checkProofOfWorkSufficient() try: + self.object.checkProofOfWorkSufficient() self.object.checkEOLSanity() self.object.checkAlreadyHave() - except (BMObjectExpiredError, BMObjectAlreadyHaveError) as e: + except (BMObjectExpiredError, BMObjectAlreadyHaveError, BMObjectInsufficientPOWError) as e: BMProto.stopDownloadingObject(self.object.inventoryHash) raise e try: From 6655e99aa35bb28b72206675d9e895f08bb7d9e4 Mon Sep 17 00:00:00 2001 From: Peter Surda Date: Fri, 20 Oct 2017 23:11:33 +0200 Subject: [PATCH 264/407] Pending download stats optimisations - tracks separately a global list for a faster sum. Needs slightly more memory --- src/network/bmproto.py | 4 ++++ src/network/objectracker.py | 2 ++ src/network/stats.py | 24 +++++++++++++----------- src/state.py | 3 +++ 4 files changed, 22 insertions(+), 11 deletions(-) diff --git a/src/network/bmproto.py b/src/network/bmproto.py index a3f7d620..5f689307 100644 --- a/src/network/bmproto.py +++ b/src/network/bmproto.py @@ -534,6 +534,10 @@ class BMProto(AdvancedDispatcher, ObjectTracker): del connection.objectsNewToThem[hashId] except KeyError: pass + try: + del state.missingObjects[hashId] + except KeyError: + pass def handle_close(self): self.set_state("close") diff --git a/src/network/objectracker.py b/src/network/objectracker.py index 62016d75..bfb75174 100644 --- a/src/network/objectracker.py +++ b/src/network/objectracker.py @@ -5,6 +5,7 @@ from threading import RLock from debug import logger from inventory import Inventory from network.dandelion import Dandelion +from state import missingObjects haveBloom = False @@ -82,6 +83,7 @@ class ObjectTracker(object): except KeyError: pass if hashId not in Inventory(): + missingObjects[hashId] = None with self.objectsNewToMeLock: self.objectsNewToMe[hashId] = True elif hashId in Dandelion().hashMap: diff --git a/src/network/stats.py b/src/network/stats.py index ade56ac0..80925f7c 100644 --- a/src/network/stats.py +++ b/src/network/stats.py @@ -2,6 +2,7 @@ import time from network.connectionpool import BMConnectionPool import asyncore_pollchoose as asyncore +from state import missingObjects lastReceivedTimestamp = time.time() lastReceivedBytes = 0 @@ -50,19 +51,20 @@ def downloadSpeed(): return currentReceivedSpeed def pendingDownload(): - tmp = {} - for connection in BMConnectionPool().inboundConnections.values() + \ - BMConnectionPool().outboundConnections.values(): - for k in connection.objectsNewToMe.keys(): - tmp[k] = True - return len(tmp) + return len(missingObjects) + #tmp = {} + #for connection in BMConnectionPool().inboundConnections.values() + \ + # BMConnectionPool().outboundConnections.values(): + # for k in connection.objectsNewToMe.keys(): + # tmp[k] = True + #return len(tmp) def pendingUpload(): - tmp = {} - for connection in BMConnectionPool().inboundConnections.values() + \ - BMConnectionPool().outboundConnections.values(): - for k in connection.objectsNewToThem.keys(): - tmp[k] = True + #tmp = {} + #for connection in BMConnectionPool().inboundConnections.values() + \ + # BMConnectionPool().outboundConnections.values(): + # for k in connection.objectsNewToThem.keys(): + # tmp[k] = True #This probably isn't the correct logic so it's disabled #return len(tmp) return 0 diff --git a/src/state.py b/src/state.py index c9cb3d1c..32433e2d 100644 --- a/src/state.py +++ b/src/state.py @@ -43,6 +43,9 @@ trustedPeer = None discoveredPeers = {} +# tracking pending downloads globally, for stats +missingObjects = {} + Peer = collections.namedtuple('Peer', ['host', 'port']) def resetNetworkProtocolAvailability(): From b025624f2a9111710e7ecbba3be020af49b317ea Mon Sep 17 00:00:00 2001 From: Peter Surda Date: Fri, 20 Oct 2017 23:21:25 +0200 Subject: [PATCH 265/407] missingObjects fix - didn't notice valid objects arriving (only invalid) --- src/network/bmproto.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/network/bmproto.py b/src/network/bmproto.py index 5f689307..9bc60af7 100644 --- a/src/network/bmproto.py +++ b/src/network/bmproto.py @@ -359,6 +359,11 @@ class BMProto(AdvancedDispatcher, ObjectTracker): objectProcessorQueue.put((self.object.objectType, buffer(self.object.data))) except BMObjectInvalidError as e: BMProto.stopDownloadingObject(self.object.inventoryHash, True) + else: + try: + del state.missingObjects[self.object.inventoryHash] + except KeyError: + pass Inventory()[self.object.inventoryHash] = ( self.object.objectType, self.object.streamNumber, buffer(self.payload[objectOffset:]), self.object.expiresTime, buffer(self.object.tag)) From 8b06fdf648842165c1b29fdc49eaa62411aa63c3 Mon Sep 17 00:00:00 2001 From: Peter Surda Date: Sat, 21 Oct 2017 21:55:12 +0200 Subject: [PATCH 266/407] checkdeps OS version handler fix - it didn't work if OS version didn't contain a quote --- checkdeps.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/checkdeps.py b/checkdeps.py index 65f8a787..05f00944 100644 --- a/checkdeps.py +++ b/checkdeps.py @@ -111,7 +111,7 @@ def detectOSRelease(): detectOS.result = None if line.startswith("VERSION_ID="): try: - version = float(line.split("\"")[1]) + version = float(line.split("=")[1].replace("\"", "")) except ValueError: pass if detectOS.result == "Ubuntu" and version < 14: From 75a6f605c1a4c903ec0e3f212dff3c6d54d53c9a Mon Sep 17 00:00:00 2001 From: Peter Surda Date: Sun, 22 Oct 2017 11:32:37 +0200 Subject: [PATCH 267/407] Download optimisation - more accurate tracking - randomise download order - longer cycle --- src/network/downloadthread.py | 25 ++++++++++++++++--------- src/network/objectracker.py | 3 ++- 2 files changed, 18 insertions(+), 10 deletions(-) diff --git a/src/network/downloadthread.py b/src/network/downloadthread.py index 98b6df05..7cbe11ad 100644 --- a/src/network/downloadthread.py +++ b/src/network/downloadthread.py @@ -9,9 +9,10 @@ from helper_threading import StoppableThread #from inventory import Inventory from network.connectionpool import BMConnectionPool import protocol +from state import missingObjects class DownloadThread(threading.Thread, StoppableThread): - maxPending = 200 + minPending = 200 requestChunk = 1000 requestTimeout = 60 cleanInterval = 60 @@ -22,13 +23,18 @@ class DownloadThread(threading.Thread, StoppableThread): self.initStop() self.name = "Downloader" logger.info("init download thread") - self.pending = {} self.lastCleaned = time.time() def cleanPending(self): deadline = time.time() - DownloadThread.requestExpires - self.pending = {k: v for k, v in self.pending.iteritems() if v >= deadline} - self.lastCleaned = time.time() + try: + toDelete = [k for k, v in missingObjects.iteritems() if v < deadline] + except RuntimeError: + pass + else: + for i in toDelete: + del missingObjects[i] + self.lastCleaned = time.time() def run(self): while not self._stopped: @@ -41,11 +47,12 @@ class DownloadThread(threading.Thread, StoppableThread): timedOut = now - DownloadThread.requestTimeout # this may take a while, but it needs a consistency so I think it's better to lock a bigger chunk with i.objectsNewToMeLock: - downloadPending = len(list((k for k, v in i.objectsNewToMe.iteritems() if k in self.pending and self.pending[k] > timedOut))) - if downloadPending >= DownloadThread.maxPending: + downloadPending = len(list((k for k, v in i.objectsNewToMe.iteritems() if k in missingObjects and missingObjects[k] > timedOut))) + if downloadPending >= DownloadThread.minPending: continue # keys with True values in the dict - request = list((k for k, v in i.objectsNewToMe.iteritems() if k not in self.pending or self.pending[k] < timedOut)) + request = list((k for k, v in i.objectsNewToMe.iteritems() if k not in missingObjects or missingObjects[k] < timedOut)) + random.shuffle(request) if not request: continue if len(request) > DownloadThread.requestChunk - downloadPending: @@ -53,7 +60,7 @@ class DownloadThread(threading.Thread, StoppableThread): # mark them as pending for k in request: i.objectsNewToMe[k] = False - self.pending[k] = now + missingObjects[k] = now payload = bytearray() payload.extend(addresses.encodeVarint(len(request))) @@ -65,4 +72,4 @@ class DownloadThread(threading.Thread, StoppableThread): if time.time() >= self.lastCleaned + DownloadThread.cleanInterval: self.cleanPending() if not requested: - self.stop.wait(1) + self.stop.wait(5) diff --git a/src/network/objectracker.py b/src/network/objectracker.py index bfb75174..a86ec23f 100644 --- a/src/network/objectracker.py +++ b/src/network/objectracker.py @@ -83,7 +83,8 @@ class ObjectTracker(object): except KeyError: pass if hashId not in Inventory(): - missingObjects[hashId] = None + if hashId not in missingObjects: + missingObjects[hashId] = time.time() with self.objectsNewToMeLock: self.objectsNewToMe[hashId] = True elif hashId in Dandelion().hashMap: From 4b40d4bce1a9a23abda072d502a5e2adc89e7498 Mon Sep 17 00:00:00 2001 From: Peter Surda Date: Sun, 22 Oct 2017 15:28:30 +0200 Subject: [PATCH 268/407] Download thread error handling --- src/network/downloadthread.py | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/src/network/downloadthread.py b/src/network/downloadthread.py index 7cbe11ad..414aacd2 100644 --- a/src/network/downloadthread.py +++ b/src/network/downloadthread.py @@ -47,11 +47,17 @@ class DownloadThread(threading.Thread, StoppableThread): timedOut = now - DownloadThread.requestTimeout # this may take a while, but it needs a consistency so I think it's better to lock a bigger chunk with i.objectsNewToMeLock: - downloadPending = len(list((k for k, v in i.objectsNewToMe.iteritems() if k in missingObjects and missingObjects[k] > timedOut))) + try: + downloadPending = len(list((k for k, v in i.objectsNewToMe.iteritems() if k in missingObjects and missingObjects[k] > timedOut))) + except KeyError: + continue if downloadPending >= DownloadThread.minPending: continue # keys with True values in the dict - request = list((k for k, v in i.objectsNewToMe.iteritems() if k not in missingObjects or missingObjects[k] < timedOut)) + try: + request = list((k for k, v in i.objectsNewToMe.iteritems() if k not in missingObjects or missingObjects[k] < timedOut)) + except KeyError: + continue random.shuffle(request) if not request: continue From e17d33cd75f3c5110f4e66aa9352533ab2eadc37 Mon Sep 17 00:00:00 2001 From: Dmitri Bogomolov <4glitch@gmail.com> Date: Tue, 24 Oct 2017 14:02:15 +0300 Subject: [PATCH 269/407] Respect user selected sort order on "Network Status" tab --- src/bitmessageqt/networkstatus.py | 9 +++++++-- src/bitmessageqt/networkstatus.ui | 3 +++ 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/src/bitmessageqt/networkstatus.py b/src/bitmessageqt/networkstatus.py index e9073cb8..67284495 100644 --- a/src/bitmessageqt/networkstatus.py +++ b/src/bitmessageqt/networkstatus.py @@ -13,12 +13,18 @@ import widgets from network.connectionpool import BMConnectionPool + class NetworkStatus(QtGui.QWidget, RetranslateMixin): def __init__(self, parent=None): super(NetworkStatus, self).__init__(parent) widgets.load('networkstatus.ui', self) - self.tableWidgetConnectionCount.horizontalHeader().setResizeMode(QtGui.QHeaderView.ResizeToContents) + header = self.tableWidgetConnectionCount.horizontalHeader() + header.setResizeMode(QtGui.QHeaderView.ResizeToContents) + + # Somehow this value was 5 when I tested + if header.sortIndicatorSection() > 4: + header.setSortIndicator(0, QtCore.Qt.AscendingOrder) self.startup = time.localtime() self.labelStartupTime.setText(_translate("networkstatus", "Since startup on %1").arg( @@ -135,7 +141,6 @@ class NetworkStatus(QtGui.QWidget, RetranslateMixin): break self.tableWidgetConnectionCount.setUpdatesEnabled(True) self.tableWidgetConnectionCount.setSortingEnabled(True) - self.tableWidgetConnectionCount.horizontalHeader().setSortIndicator(0, QtCore.Qt.AscendingOrder) self.labelTotalConnections.setText(_translate( "networkstatus", "Total Connections: %1").arg(str(self.tableWidgetConnectionCount.rowCount()))) # FYI: The 'singlelistener' thread sets the icon color to green when it receives an incoming connection, meaning that the user's firewall is configured correctly. diff --git a/src/bitmessageqt/networkstatus.ui b/src/bitmessageqt/networkstatus.ui index f05e5f62..e0c01b57 100644 --- a/src/bitmessageqt/networkstatus.ui +++ b/src/bitmessageqt/networkstatus.ui @@ -97,6 +97,9 @@ QAbstractItemView::NoSelection + + true + true From 8b7b91b23d3fbd6b9f264f727988852ea256e8d8 Mon Sep 17 00:00:00 2001 From: PyBitmessage Translations Date: Tue, 31 Oct 2017 09:40:18 +0100 Subject: [PATCH 270/407] Auto-updated language de from transifex --- src/translations/bitmessage_de.qm | Bin 98426 -> 98493 bytes src/translations/bitmessage_de.ts | 340 +++++++++++++++--------------- 2 files changed, 175 insertions(+), 165 deletions(-) diff --git a/src/translations/bitmessage_de.qm b/src/translations/bitmessage_de.qm index 63ccd5254141b427e57228bcf0ec9e72a1c935d7..c04e1950848d63fcd660fbaab722484281145989 100644 GIT binary patch delta 3396 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 SiD)}(N_6z`C2s7~fd2!gn)eL= delta 3340 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 U8WI>`F*h(OD&%N|Gy6F7{}wCvM*si- diff --git a/src/translations/bitmessage_de.ts b/src/translations/bitmessage_de.ts index df53838a..1dd83139 100644 --- a/src/translations/bitmessage_de.ts +++ b/src/translations/bitmessage_de.ts @@ -58,12 +58,12 @@ EmailGatewayRegistrationDialog - + Registration failed: Registrierung fehlgeschlagen: - + The requested email address is not available, please try a new one. Fill out the new desired email address (including @mailchuck.com) below: Die gewünschte E-Mailaddresse ist nicht verfügbar, bitte probieren Sie eine neue. Die gewünschte E-Mailaddresse (inkl. @mailchuck.com) unten ausfüllen: @@ -289,7 +289,7 @@ Bitte geben Sie die gewünschte E-Mail-Adresse (inkl. @mailchuck.com) unten ein: Eine Ihrer Adressen, %1, ist eine alte Adresse der Version 1 und wird nicht mehr unterstützt. Soll sie jetzt gelöscht werden? - + Waiting for their encryption key. Will request it again soon. Warte auf den Verschlüsselungscode. Wird bald erneut angefordert. @@ -299,17 +299,17 @@ Bitte geben Sie die gewünschte E-Mail-Adresse (inkl. @mailchuck.com) unten ein: - + Queued. In Warteschlange. - + Message sent. Waiting for acknowledgement. Sent at %1 Nachricht gesendet. Warte auf Bestätigung. Zeitpunkt der Sendung: %1 - + Message sent. Sent at %1 Nachricht gesendet. Zeitpunkt der Sendung: %1 @@ -319,47 +319,47 @@ Bitte geben Sie die gewünschte E-Mail-Adresse (inkl. @mailchuck.com) unten ein: - + Acknowledgement of the message received %1 Bestätigung der Nachricht erhalten %1 - + Broadcast queued. Rundruf in Warteschlange. - + Broadcast on %1 Rundruf um %1 - + Problem: The work demanded by the recipient is more difficult than you are willing to do. %1 Problem: Die vom Empfänger geforderte Arbeit ist schwerer als Sie bereit sind, zu berechnen. %1 - + Problem: The recipient's encryption key is no good. Could not encrypt message. %1 Problem: Der Verschlüsselungscode des Empfängers ist nicht in Ordnung. Nachricht konnte nicht verschlüsselt werden. %1 - + Forced difficulty override. Send should start soon. Schwierigkeitslimit überschrieben. Senden sollte bald beginnen. - + Unknown status: %1 %2 Unbekannter Status: %1 %2 - + Not Connected Nicht verbunden - + Show Bitmessage Bitmessage anzeigen @@ -369,12 +369,12 @@ Bitte geben Sie die gewünschte E-Mail-Adresse (inkl. @mailchuck.com) unten ein: Senden - + Subscribe Abonnieren - + Channel Chan @@ -384,12 +384,12 @@ Bitte geben Sie die gewünschte E-Mail-Adresse (inkl. @mailchuck.com) unten ein: Beenden - + You may manage your keys by editing the keys.dat file stored in the same directory as this program. It is important that you back up this file. Sie können Ihre Schlüssel verwalten, indem Sie die keys.dat bearbeiten, die im gleichen Ordner wie das Programm liegt. Es ist empfehlenswert, vorher ein Backup dieser Datei anzulegen. - + You may manage your keys by editing the keys.dat file stored in %1 It is important that you back up this file. @@ -398,17 +398,17 @@ It is important that you back up this file. Es ist empfehlenswert, vorher ein Backup dieser Datei anzulegen. - + Open keys.dat? Die keys.dat öffnen? - + You may manage your keys by editing the keys.dat file stored in the same directory as this program. It is important that you back up this file. Would you like to open the file now? (Be sure to close Bitmessage before making any changes.) Sie können Ihre Schlüssel verwalten, indem Sie die keys.dat bearbeiten, die im gleichen Ordner wie das Programm liegt. Es ist empfehlenswert, vorher ein Backup dieser Datei anzulegen. Möchten Sie die Datei jetzt öffnen? (Stellen Sie sicher, dass Sie Bitmessage beendet haben, bevor Sie etwas ändern.) - + You may manage your keys by editing the keys.dat file stored in %1 It is important that you back up this file. Would you like to open the file now? (Be sure to close Bitmessage before making any changes.) @@ -418,37 +418,37 @@ Es ist empfehlenswert, vorher ein Backup dieser Datei anzulegen. Möchten Sie di (Stellen Sie sicher, dass Sie Bitmessage beendet haben, bevor Sie etwas ändern.) - + Delete trash? Papierkorb leeren? - + Are you sure you want to delete all trashed messages? Sind Sie sicher, dass Sie alle Nachrichten im Papierkorb löschen möchten? - + bad passphrase Falsches Passwort - + You must type your passphrase. If you don't have one then this is not the form for you. Sie müssen Ihr Passwort eingeben. Wenn Sie keins haben, ist dies das falsche Formular für Sie. - + Bad address version number Falsche Addressenversionsnummer - + Your address version number must be a number: either 3 or 4. Die Addressenversionsnummer muss eine Zahl sein, entweder 3 oder 4. - + Your address version number must be either 3 or 4. Die Addressenversionnsnummer muss entweder 3 oder 4 sein. @@ -518,22 +518,22 @@ Es ist empfehlenswert, vorher ein Backup dieser Datei anzulegen. Möchten Sie di - + Connection lost Verbindung verloren - + Connected Verbunden - + Message trashed Nachricht in den Papierkorb verschoben - + The TTL, or Time-To-Live is the length of time that the network will hold the message. The recipient must get it during this time. If your Bitmessage client does not hear an acknowledgement, it will resend the message automatically. The longer the Time-To-Live, the @@ -541,17 +541,17 @@ Es ist empfehlenswert, vorher ein Backup dieser Datei anzulegen. Möchten Sie di Die Haltbarkeit, oder Time-To-Live, ist die Dauer, für die das Netzwerk die Nachricht speichern wird. Der Empfänger muss sie während dieser Zeit empfangen. Wenn Ihr Bitmessage-Client keine Empfangsbestätigung erhält, wird die Nachricht automatisch erneut verschickt. Je länger die Time-To-Live, desto mehr Arbeit muss Ihr Rechner verrichten, um die Nachricht zu senden. Eine Time-To-Live von vier oder fünf Tagen ist meist ausreichend. - + Message too long Narchricht zu lang - + The message that you are trying to send is too long by %1 bytes. (The maximum is 261644 bytes). Please cut it down before sending. Die Nachricht, die Sie zu senden versuchen, ist %1 Byte zu lang. (Maximum 261.644 Bytes). Bitte verringern Sie ihre Größe vor dem Senden. - + Error: Your account wasn't registered at an email gateway. Sending registration now as %1, please wait for the registration to be processed before retrying sending. Fehler: Ihr Konto war an keiner E-Mail Schnittstelle registriert. Registrierung als %1 wird versandt, bitte vor einem erneutem Sendeversuch auf die Registrierungsverarbeitung warten. @@ -596,57 +596,57 @@ Es ist empfehlenswert, vorher ein Backup dieser Datei anzulegen. Möchten Sie di - + Error: You must specify a From address. If you don't have one, go to the 'Your Identities' tab. Fehler: Sie müssen eine Absenderadresse auswählen. Sollten Sie keine haben, wechseln Sie zum Reiter "Ihre Identitäten". - + Address version number Adressversion - + Concerning the address %1, Bitmessage cannot understand address version numbers of %2. Perhaps upgrade Bitmessage to the latest version. Aufgrund der Adresse %1 kann Bitmessage Adressen mit der Version %2 nicht verarbeiten. Möglicherweise müssen Sie Bitmessage auf die aktuelle Version aktualisieren. - + Stream number Datenstrom Nummer - + Concerning the address %1, Bitmessage cannot handle stream numbers of %2. Perhaps upgrade Bitmessage to the latest version. Aufgrund der Adresse %1 kann Bitmessage den Datenstrom mit der Version %2 nicht verarbeiten. Möglicherweise müssen Sie Bitmessage auf die aktuelle Version aktualisieren. - + Warning: You are currently not connected. Bitmessage will do the work necessary to send the message but it won't send until you connect. Warnung: Sie sind aktuell nicht verbunden. Bitmessage wird die nötige Arbeit zum versenden verrichten, aber erst senden, wenn Sie verbunden sind. - + Message queued. Nachricht befindet sich in der Warteschleife. - + Your 'To' field is empty. Ihr "Empfänger"-Feld ist leer. - + Right click one or more entries in your address book and select 'Send message to this address'. Klicken Sie mit rechts auf einen oder mehrere Einträge aus Ihrem Adressbuch und wählen Sie "Nachricht an diese Adresse senden". - + Fetched address from namecoin identity. Adresse aus Namecoin Identität geholt. - + New Message Neue Nachricht @@ -656,7 +656,7 @@ Es ist empfehlenswert, vorher ein Backup dieser Datei anzulegen. Möchten Sie di - + Sending email gateway registration request Der Registrierungsantrag für die E-Mail Schnittstelle wird versandt. @@ -671,142 +671,142 @@ Es ist empfehlenswert, vorher ein Backup dieser Datei anzulegen. Möchten Sie di Die von Ihnen eingegebene Adresse ist ungültig, sie wird ignoriert. - + Error: You cannot add the same address to your address book twice. Try renaming the existing one if you want. Fehler: Sie können eine Adresse nicht doppelt im Adressbuch speichern. Sie können jedoch die bereits eingetragene umbenennen. - + Error: You cannot add the same address to your subscriptions twice. Perhaps rename the existing one if you want. Fehler: Dieselbe Adresse kann nicht doppelt in die Abonnements eingetragen werden. Sie können jedoch die bereits eingetragene umbenennen. - + Restart Neustart - + You must restart Bitmessage for the port number change to take effect. Sie müssen Bitmessage neu starten, um den geänderten Port zu verwenden. - + Bitmessage will use your proxy from now on but you may want to manually restart Bitmessage now to close existing connections (if any). Bitmessage wird ab sofort den Proxy-Server verwenden, aber eventuell möchten Sie Bitmessage neu starten um bereits bestehende Verbindungen zu schließen. - + Number needed Zahl erforderlich - + Your maximum download and upload rate must be numbers. Ignoring what you typed. Ihre maximale Herungerlade- und Hochladegeschwindigkeit müssen Zahlen sein. Die eingetragenen Werte werden ignoriert. - + Will not resend ever Wird nie wiederversendet - + Note that the time limit you entered is less than the amount of time Bitmessage waits for the first resend attempt therefore your messages will never be resent. Bitte beachten Sie, dass der eingetratene Dauer kürzer ist als die, die Bitmessage auf das erste Wiederversenden wartet. Deswegen werden Ihre Nachrichten nie wiederversendet. - + Sending email gateway unregistration request E-Mail Schnittestellen-Abmeldeantrag wird versandt - + Sending email gateway status request E-Mail Schnittestellen Statusantrag wird versandt - + Passphrase mismatch Kennwort stimmt nicht überein - + The passphrase you entered twice doesn't match. Try again. Die von Ihnen eingegebenen Kennwörter sind nicht identisch. Bitte neu versuchen. - + Choose a passphrase Wählen Sie ein Kennwort - + You really do need a passphrase. Sie benötigen wirklich ein Kennwort. - + Address is gone Adresse ist verloren - + Bitmessage cannot find your address %1. Perhaps you removed it? Bitmessage kann Ihre Adresse %1 nicht finden. Haben Sie sie gelöscht? - + Address disabled Adresse deaktiviert - + Error: The address from which you are trying to send is disabled. You'll have to enable it on the 'Your Identities' tab before using it. Fehler: Die Adresse von der Sie versuchen zu senden ist deaktiviert. Sie müssen sie unter dem Reiter "Ihre Identitäten" aktivieren bevor Sie fortfahren. - + Entry added to the Address Book. Edit the label to your liking. Eintrag dem Adressbuch hinzugefügt. Editieren Sie den Eintrag nach Belieben. - + Entry added to the blacklist. Edit the label to your liking. Eintrag in die Blacklist hinzugefügt. Die Beschriftung können Sie ändern. - + Error: You cannot add the same address to your blacklist twice. Try renaming the existing one if you want. Fehler: Dieselbe Addresse kann nicht doppelt in die Blacklist eingetragen werden. Sie können jedoch die bereits eingetragene umbenennen. - + Moved items to trash. Objekt(e) in den Papierkorb verschoben. - + Undeleted item. Nachricht wiederhergestellt. - + Save As... Speichern unter... - + Write error. Fehler beim Speichern. - + No addresses selected. Keine Adresse ausgewählt. - + If you delete the subscription, messages that you already received will become inaccessible. Maybe you can consider disabling the subscription instead. Disabled subscriptions will not receive new messages, but you can still view messages you already received. Are you sure you want to delete the subscription? @@ -815,7 +815,7 @@ Are you sure you want to delete the subscription? Sind Sie sicher, dass Sie das Abonnement löschen möchten? - + If you delete the channel, messages that you already received will become inaccessible. Maybe you can consider disabling the channel instead. Disabled channels will not receive new messages, but you can still view messages you already received. Are you sure you want to delete the channel? @@ -824,92 +824,92 @@ Are you sure you want to delete the channel? Sind Sie sicher, dass Sie das Chan löschen möchten? - + Do you really want to remove this avatar? Wollen Sie diesen Avatar wirklich entfernen? - + You have already set an avatar for this address. Do you really want to overwrite it? Sie haben bereits einen Avatar für diese Adresse gewählt. Wollen Sie ihn wirklich überschreiben? - + Start-on-login not yet supported on your OS. Mit Betriebssystem starten, noch nicht von Ihrem Betriebssystem unterstützt - + Minimize-to-tray not yet supported on your OS. Ins System Tray minimieren von Ihrem Betriebssytem noch nicht unterstützt. - + Tray notifications not yet supported on your OS. Trach-Benachrichtigungen von Ihrem Betriebssystem noch nicht unterstützt. - + Testing... teste... - + This is a chan address. You cannot use it as a pseudo-mailing list. Dies ist eine Chan-Adresse. Sie können sie nicht als Pseudo-Mailingliste verwenden. - + The address should start with ''BM-'' Die Adresse sollte mit "BM-" beginnen - + The address is not typed or copied correctly (the checksum failed). Die Adresse wurde nicht korrekt getippt oder kopiert (Prüfsumme falsch). - + The version number of this address is higher than this software can support. Please upgrade Bitmessage. Die Versionsnummer dieser Adresse ist höher als diese Software unterstützt. Bitte installieren Sie die neuste Bitmessage Version. - + The address contains invalid characters. Diese Adresse beinhaltet ungültige Zeichen. - + Some data encoded in the address is too short. Die in der Adresse codierten Daten sind zu kurz. - + Some data encoded in the address is too long. Die in der Adresse codierten Daten sind zu lang. - + Some data encoded in the address is malformed. Einige in der Adresse kodierten Daten sind ungültig. - + Enter an address above. Eine Addresse oben ausfüllen. - + Address is an old type. We cannot display its past broadcasts. Alter Addressentyp. Wir können deren vorige Rundrufe nicht anzeigen. - + There are no recent broadcasts from this address to display. Es gibt keine neuen Rundrufe von dieser Adresse die angezeigt werden können. - + You are using TCP port %1. (This can be changed in the settings). Sie benutzen TCP-Port %1 (Dieser kann in den Einstellungen verändert werden). @@ -1119,47 +1119,47 @@ Sind Sie sicher, dass Sie das Chan löschen möchten? Neuen Eintrag erstellen - + Display the %1 recent broadcast(s) from this address. Die letzten %1 Rundruf(e) von dieser Addresse anzeigen. - + New version of PyBitmessage is available: %1. Download it from https://github.com/Bitmessage/PyBitmessage/releases/latest Neue Version von PyBitmessage steht zur Verfügung: %1. Sie können sie von https://github.com/Bitmessage/PyBitmessage/releases/latest herunterladen. - + Waiting for PoW to finish... %1% Warte auf Abschluss von Berechnungen (PoW)... %1% - + Shutting down Pybitmessage... %1% PyBitmessage wird beendet... %1% - + Waiting for objects to be sent... %1% Warte auf Versand von Objekten... %1% - + Saving settings... %1% Einstellungen werden gespeichert... %1% - + Shutting down core... %1% Kern wird beendet... %1% - + Stopping notifications... %1% Beende Benachrichtigungen... %1% - + Shutdown imminent... %1% Unmittelbar vor Beendung... %1% @@ -1174,12 +1174,12 @@ Sind Sie sicher, dass Sie das Chan löschen möchten? %n Tag%n Tage - + Shutting down PyBitmessage... %1% PyBitmessage wird beendet... %1% - + Sent Gesendet @@ -1282,7 +1282,7 @@ Receiver's required difficulty: %1 and %2 Problem: Sie versuchen, eine Nachricht an sich zu versenden, aber Ihr Schlüssel befindet sich nicht in der keys.dat-Datei. Die Nachricht kann nicht verschlüsselt werden. 1% - + Doing work necessary to send message. Arbeit wird verrichtet, um die Nachricht zu verschicken. @@ -1292,7 +1292,7 @@ Receiver's required difficulty: %1 and %2 Nachricht gesendet. Auf Bestätigung wird gewartet. Zeitpunkt der Sendung: %1 - + Doing work necessary to request encryption key. Arbeit wird verrichtet, um den Schlüssel nachzufragen... @@ -1322,32 +1322,32 @@ Receiver's required difficulty: %1 and %2 Alle Nachrichten als gelesen markieren - + Are you sure you would like to mark all messages read? Sind Sie sicher, dass Sie alle Nachrichten als gelesen markieren möchten? - + Doing work necessary to send broadcast. Führe Arbeit aus, die notwendig ist zum Senden des Rundspruches. - + Proof of work pending Arbeitsbeweis wird berechnet - + %n object(s) pending proof of work %n Objekt wartet auf Berechnungen%n Objekte warten auf Berechnungen - + %n object(s) waiting to be distributed %n Objekt wartet darauf, verteilt zu werden%n Objekte warten darauf, verteilt zu werden - + Wait until these tasks finish? Warten bis diese Aufgaben erledigt sind? @@ -1412,7 +1412,7 @@ Receiver's required difficulty: %1 and %2 Ihre Grafikkarte hat inkorrekt berechnet, OpenCL wird deaktiviert. Bitte benachrichtigen Sie die Entwickler. - + Set notification sound... Benachrichtigungsklang einstellen ... @@ -1436,92 +1436,102 @@ Willkommen zu einfachem und sicherem Bitmessage für Chans nicht empfohlen - + + Quiet Mode + + + + Problems connecting? Try enabling UPnP in the Network Settings Verbindungsprobleme? Versuchen Sie UPnP in den Netzwerkeinstellungen einzuschalten - + You are trying to send an email instead of a bitmessage. This requires registering with a gateway. Attempt to register? Sie versuchen, eine E-Mail anstelle einer Bitmessage zu senden. Dies erfordert eine Registrierung bei einer Schnittstelle. Registrierung versuchen? - + Error: Bitmessage addresses start with BM- Please check the recipient address %1 Fehler: Bitmessage Adressen starten mit BM- Bitte überprüfen Sie die Empfängeradresse %1 - + Error: The recipient address %1 is not typed or copied correctly. Please check it. Fehler: Die Empfängeradresse %1 wurde nicht korrekt getippt oder kopiert. Bitte überprüfen. - + Error: The recipient address %1 contains invalid characters. Please check it. Fehler: Die Empfängeradresse %1 beinhaltet ungültig Zeichen. Bitte überprüfen. - + Error: The version of the recipient address %1 is too high. Either you need to upgrade your Bitmessage software or your acquaintance is being clever. Fehler: Die Empfängerdresseversion von %1 ist zu hoch. Entweder Sie müssen Ihre Bitmessage Software aktualisieren oder Ihr Bekannter ist sehr clever. - + Error: Some data encoded in the recipient address %1 is too short. There might be something wrong with the software of your acquaintance. Fehler: Einige Daten die in der Empfängerdresse %1 codiert sind, sind zu kurz. Es könnte sein, dass etwas mit der Software Ihres Bekannten nicht stimmt. - + Error: Some data encoded in the recipient address %1 is too long. There might be something wrong with the software of your acquaintance. Fehler: Einige Daten die in der Empfängeradresse %1 codiert sind, sind zu lang. Es könnte sein, dass etwas mit der Software Ihres Bekannten nicht stimmt. - + Error: Some data encoded in the recipient address %1 is malformed. There might be something wrong with the software of your acquaintance. Fehler: Einige codierte Daten in der Empfängeradresse %1 sind ungültig. Es könnte etwas mit der Software Ihres Bekannten sein. - + Error: Something is wrong with the recipient address %1. Fehler: Mit der Empfängeradresse %1 stimmt etwas nicht. - + + Error: %1 + Fehler: %1 + + + From %1 Von %1 - + Synchronisation pending Synchronisierung läuft - + Bitmessage hasn't synchronised with the network, %n object(s) to be downloaded. If you quit now, it may cause delivery delays. Wait until the synchronisation finishes? Bitmessage ist nicht synchronisiert, %n Objekt wurde noch nicht heruntergeladen. Wenn Sie das Programm jetzt beenden, kann es zu Zustellverzögerungen kommen. Warten bis die Synchronisierung abgeschlossen ist?Bitmessage ist nicht synchronisiert, %n Objekte wurden noch nicht heruntergeladen. Wenn Sie das Programm jetzt beenden, kann es zu Zustellverzögerungen kommen. Warten bis die Synchronisierung abgeschlossen ist? - + Not connected Nicht verbunden - + Bitmessage isn't connected to the network. If you quit now, it may cause delivery delays. Wait until connected and the synchronisation finishes? Bitmessage ist nicht mit dem Netzwerk verbunden. Wenn Sie das Programm jetzt beenden, kann es zu Zustellverzögerungen kommen. Warten bis eine Verbindung besteht und die Synchronisierung abgeschlossen ist? - + Waiting for network connection... Warte auf Netzwerkverbindung... - + Waiting for finishing synchronisation... Warte auf Synchronisationsabschluss... - + You have already set a notification sound for this address book entry. Do you really want to overwrite it? Sie haben bereits einen Benachrichtigungsklang für diesen Adressbucheintrag gesetzt. Möchten Sie ihn wirklich überschreiben? @@ -1868,37 +1878,37 @@ Die Zufallszahlen-Option ist standardmässig gewählt, jedoch haben deterministi Verbindungen insgesamt: - + Since startup: Seit Start: - + Processed 0 person-to-person messages. 0 Person-zu-Person-Nachrichten verarbeitet. - + Processed 0 public keys. 0 öffentliche Schlüssel verarbeitet. - + Processed 0 broadcasts. 0 Rundrufe verarbeitet. - + Inventory lookups per second: 0 Inventory lookups pro Sekunde: 0 - + Objects to be synced: Zu synchronisierende Objektanzahl: - + Stream # Datenstrom # @@ -1908,37 +1918,37 @@ Die Zufallszahlen-Option ist standardmässig gewählt, jedoch haben deterministi - + Since startup on %1 Seit Start der Anwendung am %1 - + Down: %1/s Total: %2 Herunter: %1/s Insg.: %2 - + Up: %1/s Total: %2 Hoch: %1/s Insg.: %2 - + Total Connections: %1 Verbindungen insgesamt: %1 - + Inventory lookups per second: %1 Inventory lookups pro Sekunde: %1 - + Up: 0 kB/s Hoch: 0 kB/s - + Down: 0 kB/s Herunter: 0 kB/s @@ -1948,72 +1958,72 @@ Die Zufallszahlen-Option ist standardmässig gewählt, jedoch haben deterministi Netzwerkstatus - + byte(s) ByteBytes - + Object(s) to be synced: %n %n Objekt zu synchronisieren.%n Objekte zu synchronisieren. - + Processed %n person-to-person message(s). %n Person-zu-Person-Nachricht bearbeitet.%n Person-zu-Person-Nachrichten bearbeitet. - + Processed %n broadcast message(s). %n Rundruf-Nachricht bearbeitet.%n Rundruf-Nachrichten bearbeitet. - + Processed %n public key(s). %n öffentlicher Schlüssel verarbeitet.%n öffentliche Schlüssel verarbeitet. - + Peer Peer - + IP address or hostname IP-Adresse oder Hostname - + Rating Bewertung - + PyBitmessage tracks the success rate of connection attempts to individual nodes. The rating ranges from -1 to 1 and affects the likelihood of selecting the node in the future PyBitmessage verfolgt die Erfolgsrate von Verbindungsversuchen zu einzelnen Knoten. Die Bewertung reicht von -1 bis 1 und beeinflusst die Wahrscheinlichkeit, den Knoten zukünftig auszuwählen - + User agent Benutzer-Agent - + Peer's self-reported software Peer's selbstberichtete Software - + TLS TLS - + Connection encryption Verbindungsverschlüsselung - + List of streams negotiated between you and the peer Liste der zwischen Ihnen und dem Peer ausgehandelter Datenströme @@ -2072,7 +2082,7 @@ Die Zufallszahlen-Option ist standardmässig gewählt, jedoch haben deterministi - Chan passhphrase/name: + Chan passphrase/name: Chan-Name @@ -2094,17 +2104,17 @@ Die Zufallszahlen-Option ist standardmässig gewählt, jedoch haben deterministi newchandialog - + Successfully created / joined chan %1 Chan %1 erfolgreich erstellt/beigetreten - + Chan creation / joining failed Chan-erstellung/-beitritt fehlgeschlagen - + Chan creation / joining cancelled Chan-erstellung/-beitritt abgebrochen From d6e94cf77ffa2b1a7460421c02ee77741f36b235 Mon Sep 17 00:00:00 2001 From: PyBitmessage Translations Date: Thu, 2 Nov 2017 12:42:50 +0100 Subject: [PATCH 271/407] Auto-updated language zh_cn from transifex --- src/translations/bitmessage_zh_cn.qm | Bin 57332 -> 57717 bytes src/translations/bitmessage_zh_cn.ts | 440 ++++++++++++++------------- 2 files changed, 235 insertions(+), 205 deletions(-) diff --git a/src/translations/bitmessage_zh_cn.qm b/src/translations/bitmessage_zh_cn.qm index 77b56321fd51c739be799b37851bf168ca4230b0..cc37b14624423f37136395a23ad72d84c3370e09 100644 GIT binary patch delta 3833 zcmY*b2~bp57Cqhld*22@MTppmN{j*`21O7JA_^KBaRU@tl$J$kltl!WcEiY`pol0E zR7AxE5=4!`2#B~xT%&^-9TK-p$`~`QaSYBOOr>gy>bn2)-@D7X=brbTH%eYKOA2i) zlK>0>YTW=~1K_y@kbVKg?*jat0J|n&>RDi%ClF>0>`ee>4&?VUK-^g1`YZ@n`U76c z5ZYz|BYhz}b_Kj=blGiFAf49?R6K$-UjTwDA>Hx@h#UuL8TVVJeZYsQK9NaJ_!S4Lq6S)_D3lFzGfm(&{ebo9}R$&-!mZR6|8DE0#$af%Uln{PKU#; z0wB2oeXn-_9(8b3jAF7C7-YwNv(LlnLT})7B!--6g#{Cr!fiu2U=G6Yo2!8Qav?|BH*Vf!26F8xLL)DOA(Zi1)Q|S^~8Dgo*SC#Eev;*;fAwHW{K_u2<%OVG$EC@T}y8)9|qVgD9)$}b6t|PJ~ z$58Ff?_1q)SHGWW7|dV4`wSGGDHJ3ec1x3$Svw;pfq3Pmdbr|*NZ-0#sn_}i9D@p z$e$#UPxx^93lkY{a(};cQB)WcFSsk3Z2=wo9~Q;ErGttJk$D6w4>gN&X1M^1+(gB- zgueTJ(YER=BK4l=cqKV8tyFYk=srO7OP3wgEV`(1CqY9*uXi)x$QIE%u`gTpN-X_` zfg@F7P0}M^S-yDC=(~XL0I_ouk@jCAwm7$UK1>k1W~?M>{KZ3F)9&ai_G(|jg>~Zb z%W20B6~}$@0yq~cPINxPKzqbVYt4WmUu;>L4D?osb8lJ!UnGe0JHOA+iPz140xWnW z-X3)y==nyxC!FiOwb_3OIX?Ni4sk|6u9gl(Ox|S^!ibv&t~h^dq}Jg-6J9a5<}T27LX(9>#>W|Fi+y_ z%P~&uA#uH4%S4S5cMbhlmq~^tFJtQz5^tk{{w#SCpU5^KJxCI;KmZb2Byn%q0@tUK zOqX`R+Fr8taviXEyJYLz2p~LEQXY}bgo`EhnieK3lUyj_`7gUa|jWbIYjbg5fe!BmApPiGFt+B3i{sbfyx*`ADIiZ6bL=`hmoBt z1@EuM0;l#0zDD+X?HP`qw`P7{D%@^-My0b7?q)aurVpf|@0rjC zp;G5!+K4LY@TXK$zk2D23r~UVi>01%5@6Of>6qt6ps&AlV*3qd($7yCv80I}d?AbMGayg-Q^pq}lYX#~zN>?psqUR?`*M2#k9N8dU=h+#^U%F)k6SM?M zOP_K7;S%Y#wG2GYo_);mL-43^TNJEmR?B6&u7cl zE@V%K`N+y9JOHkpl2!iWGBDw_tg48uw0R<{9%KZDej=+|Zw=&`WnXo2Wz##^Im;{^ z@S#C=V?UMaoK|*cIx{?TT=qo9Kr#L0-3p0Nzzg|sRVz8NPwpB0AqDGqd0-pgr+Ubz z7cQk_JIPH$;@Kh(`Ks-s>90_}=F?sjpc46d_q)L0XnA3eUF5_ZM5p{j*KQm?pZaiGu)R@`1=rU11lZg1tgdy?Y*od_0qSMkf6a=>>9teZ?`5ygT~|6@WQM*`O1CYU9H%nn@bEc+&l;u2-#h_Xm~z6mbh!P# z@>9H{!i`g!){`?XS;|x+6Y<)tT=9DcTh&XMAK<`K(n?wMH-VLwC^!8wl!x3iWoaie zv-T*0y$+`tQp<5T5NQ{OUBvr1(dL0$MkrCz{kK&i?)a2X}}tjgNd3W%LmABgUg z+^1BItJsn)XI0a44s!k{PgKQ}651a5D%0anNxr43xEKb!cv+Ru&P;P!x@_GW)v|CN zQjKA%(nk!q_+P4;QE#Y#)vCIX4hl?9)z|9htSm^?)XK{Cbf``(uj6>ys;;adDMuHp zehBAuEOu7?Qo_U<8`Yw~8VgCZK&{N=I5?hBt0Y$eW1(7gKaG{2RBKoG(b}tROW9M~ zduqq$4b=WB^?(x*>}8|cRYpfyIqDIU`tx|UQjZGd_n|k`qpivQlHO|L1S)FIcYm`tRPcedfk^oGE7~j{Fo%Ws6Oz= zMw0G;`rPt(AmxJk+>bT9N+qaoZ8ZW%ep9z)J|SY(>f8RsOfXUXxEsfG%SQEM1BIrc zR^!?^9Wy`IeBwkzbA2>pk4S*P6Pi%V(s^WUrDk^PGzLo5#Jq^)A+k_o+FVX@9M{aN z9z&=;)XWd~oOiZUnzZk|cpnJXWQK`&SVd~`P8Tw%|6AV zQ)JK_>P9~{$(lpnI!d;W=1}L9#6Hv8muuQy@bcJlU-SLq>%5RW&^-T)t(*F#R`Ku%dmE>70z)$g~Wd|I1u9cAqZmTLyADs9Phd0jBKG75vI|@!fS>50gQ7t}89&tS-5y z+cDDvnCPS1X<+#wGj-pVaTd2_=~@Sqi9na_Y3{Q5|LU^E&AK+@O3q;Jsy`oa__glW z;C@8HTi4;BCi?5{-fW2sRKBAR9`cm32bQ6AL~<2Sv#vgp7w-8ep=t$op$am{WUWkIrY^)@P5Gi z*CYL#bqsj^1?S_2x~Qq&z`7;csV?@a;$C^bzOzGr3{i9fA>&W!my(j4;#oAwrDxq0 zuLf-|Rjg^n&oKPoBKf$KwMZ*AQ)(9iNtDN==Q9HMtn3#>J<47*b3N(&JN1seFiy=gO2$ zCmHc+aaM#}3Nzjk;0 EFOUd~RR910 delta 3528 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 EmailGatewayRegistrationDialog - + Registration failed: 注册失败: - + The requested email address is not available, please try a new one. Fill out the new desired email address (including @mailchuck.com) below: 要求的电子邮件地址不详,请尝试一个新的。填写新的所需电子邮件地址(包括 @mailchuck.com)如下: @@ -166,52 +166,52 @@ Please type the desired email address (including @mailchuck.com) below: MainWindow - + Reply to sender 回复发件人 - + Reply to channel 回复通道 - + Add sender to your Address Book 将发送者添加到您的通讯簿 - + Add sender to your Blacklist 将发件人添加到您的黑名单 - + Move to Trash 移入回收站 - + Undelete 取消删除 - + View HTML code as formatted text 作为HTML查看 - + Save message as... 将消息保存为... - + Mark Unread 标记为未读 - + New 新建 @@ -236,12 +236,12 @@ Please type the desired email address (including @mailchuck.com) below: 将地址复制到剪贴板 - + Special address behavior... 特别的地址行为... - + Email gateway 电子邮件网关 @@ -251,37 +251,37 @@ Please type the desired email address (including @mailchuck.com) below: 删除 - + Send message to this address 发送消息到这个地址 - + Subscribe to this address 订阅到这个地址 - + Add New Address 创建新地址 - + Copy destination address to clipboard 复制目标地址到剪贴板 - + Force send 强制发送 - + One of your addresses, %1, is an old version 1 address. Version 1 addresses are no longer supported. May we delete it now? 您的地址中的一个, %1,是一个过时的版本1地址. 版本1地址已经不再受到支持了. 我们可以将它删除掉么? - + Waiting for their encryption key. Will request it again soon. 正在等待他们的加密密钥,我们会在稍后再次请求。 @@ -291,17 +291,17 @@ Please type the desired email address (including @mailchuck.com) below: - + Queued. 已经添加到队列。 - + Message sent. Waiting for acknowledgement. Sent at %1 消息已经发送. 正在等待回执. 发送于 %1 - + Message sent. Sent at %1 消息已经发送. 发送于 %1 @@ -311,47 +311,47 @@ Please type the desired email address (including @mailchuck.com) below: - + Acknowledgement of the message received %1 消息的回执已经收到于 %1 - + Broadcast queued. 广播已经添加到队列中。 - + Broadcast on %1 已经广播于 %1 - + Problem: The work demanded by the recipient is more difficult than you are willing to do. %1 错误: 收件人要求的做工量大于我们的最大接受做工量。 %1 - + Problem: The recipient's encryption key is no good. Could not encrypt message. %1 错误: 收件人的加密密钥是无效的。不能加密消息。 %1 - + Forced difficulty override. Send should start soon. 已经忽略最大做工量限制。发送很快就会开始。 - + Unknown status: %1 %2 未知状态: %1 %2 - + Not Connected 未连接 - + Show Bitmessage 显示比特信 @@ -361,12 +361,12 @@ Please type the desired email address (including @mailchuck.com) below: 发送 - + Subscribe 订阅 - + Channel 频道 @@ -376,66 +376,66 @@ Please type the desired email address (including @mailchuck.com) below: 退出 - + You may manage your keys by editing the keys.dat file stored in the same directory as this program. It is important that you back up this file. 您可以通过编辑和程序储存在同一个目录的 keys.dat 来编辑密钥。备份这个文件十分重要。 - + You may manage your keys by editing the keys.dat file stored in %1 It is important that you back up this file. 您可以通过编辑储存在 %1 的 keys.dat 来编辑密钥。备份这个文件十分重要。 - + Open keys.dat? 打开 keys.dat ? - + You may manage your keys by editing the keys.dat file stored in the same directory as this program. It is important that you back up this file. Would you like to open the file now? (Be sure to close Bitmessage before making any changes.) 您可以通过编辑和程序储存在同一个目录的 keys.dat 来编辑密钥。备份这个文件十分重要。您现在想打开这个文件么?(请在进行任何修改前关闭比特信) - + You may manage your keys by editing the keys.dat file stored in %1 It is important that you back up this file. Would you like to open the file now? (Be sure to close Bitmessage before making any changes.) 您可以通过编辑储存在 %1 的 keys.dat 来编辑密钥。备份这个文件十分重要。您现在想打开这个文件么?(请在进行任何修改前关闭比特信) - + Delete trash? 清空回收站? - + Are you sure you want to delete all trashed messages? 您确定要删除全部被回收的消息么? - + bad passphrase 错误的密钥 - + You must type your passphrase. If you don't have one then this is not the form for you. 您必须输入您的密钥。如果您没有的话,这个表单不适用于您。 - + Bad address version number 地址的版本号无效 - + Your address version number must be a number: either 3 or 4. 您的地址的版本号必须是一个数字: 3 或 4. - + Your address version number must be either 3 or 4. 您的地址的版本号必须是 3 或 4. @@ -505,22 +505,22 @@ It is important that you back up this file. Would you like to open the file now? - + Connection lost 连接已丢失 - + Connected 已经连接 - + Message trashed 消息已经移入回收站 - + The TTL, or Time-To-Live is the length of time that the network will hold the message. The recipient must get it during this time. If your Bitmessage client does not hear an acknowledgement, it will resend the message automatically. The longer the Time-To-Live, the @@ -529,17 +529,17 @@ It is important that you back up this file. Would you like to open the file now? 收件人必须在此期间得到它. 如果您的Bitmessage客户沒有听到确认, 它会自动重新发送信息. Time-To-Live的时间越长, 您的电脑必须要做更多工作來发送信息. 四天或五天的 Time-To-Time, 经常是合适的. - + Message too long 信息太长 - + The message that you are trying to send is too long by %1 bytes. (The maximum is 261644 bytes). Please cut it down before sending. 你正在尝试发送的信息已超过%1个字节太长, (最大为261644个字节). 发送前请剪下来。 - + Error: Your account wasn't registered at an email gateway. Sending registration now as %1, please wait for the registration to be processed before retrying sending. 错误: 您的帐户没有在电子邮件网关注册。现在发送注册为%1​​, 注册正在处理请稍候重试发送. @@ -584,67 +584,67 @@ It is important that you back up this file. Would you like to open the file now? - + Error: You must specify a From address. If you don't have one, go to the 'Your Identities' tab. 错误: 您必须指出一个表单地址, 如果您没有,请到“您的身份”标签页。 - + Address version number 地址版本号 - + Concerning the address %1, Bitmessage cannot understand address version numbers of %2. Perhaps upgrade Bitmessage to the latest version. 地址 %1 的地址版本号 %2 无法被比特信理解。也许你应该升级你的比特信到最新版本。 - + Stream number 节点流序号 - + Concerning the address %1, Bitmessage cannot handle stream numbers of %2. Perhaps upgrade Bitmessage to the latest version. 地址 %1 的节点流序号 %2 无法被比特信理解。也许你应该升级你的比特信到最新版本。 - + Warning: You are currently not connected. Bitmessage will do the work necessary to send the message but it won't send until you connect. 警告: 您尚未连接。 比特信将做足够的功来发送消息,但是消息不会被发出直到您连接。 - + Message queued. 信息排队。 - + Your 'To' field is empty. “收件人"是空的。 - + Right click one or more entries in your address book and select 'Send message to this address'. 在您的地址本的一个条目上右击,之后选择”发送消息到这个地址“。 - + Fetched address from namecoin identity. 已经自namecoin接收了地址。 - + New Message 新消息 - + From - 来自 + - + Sending email gateway registration request 发送电​​子邮件网关注册请求 @@ -659,142 +659,142 @@ It is important that you back up this file. Would you like to open the file now? 您输入的地址是无效的,将被忽略。 - + Error: You cannot add the same address to your address book twice. Try renaming the existing one if you want. 错误:您无法将一个地址添加到您的地址本两次,请尝试重命名已经存在的那个。 - + Error: You cannot add the same address to your subscriptions twice. Perhaps rename the existing one if you want. 错误: 您不能在同一地址添加到您的订阅两次. 也许您可重命名现有之一. - + Restart 重启 - + You must restart Bitmessage for the port number change to take effect. 您必须重启以便使比特信对于使用的端口的改变生效。 - + Bitmessage will use your proxy from now on but you may want to manually restart Bitmessage now to close existing connections (if any). 比特信将会从现在开始使用代理,但是您可能想手动重启比特信以便使之前的连接关闭(如果有的话)。 - + Number needed 需求数字 - + Your maximum download and upload rate must be numbers. Ignoring what you typed. 您最大的下载和上传速率必须是数字. 忽略您键入的内容. - + Will not resend ever 不尝试再次发送 - + Note that the time limit you entered is less than the amount of time Bitmessage waits for the first resend attempt therefore your messages will never be resent. 请注意,您所输入的时间限制小于比特信的最小重试时间,因此您将永远不会重发消息。 - + Sending email gateway unregistration request 发送电​​子邮件网关注销请求 - + Sending email gateway status request 发送电​​子邮件网关状态请求 - + Passphrase mismatch 密钥不匹配 - + The passphrase you entered twice doesn't match. Try again. 您两次输入的密码并不匹配,请再试一次。 - + Choose a passphrase 选择一个密钥 - + You really do need a passphrase. 您真的需要一个密码。 - + Address is gone 已经失去了地址 - + Bitmessage cannot find your address %1. Perhaps you removed it? 比特信无法找到你的地址 %1。 也许你已经把它删掉了? - + Address disabled 地址已经禁用 - + Error: The address from which you are trying to send is disabled. You'll have to enable it on the 'Your Identities' tab before using it. 错误: 您想以一个您已经禁用的地址发出消息。在使用之前您需要在“您的身份”处再次启用。 - + Entry added to the Address Book. Edit the label to your liking. 条目已经添加到地址本。您可以去修改您的标签。 - + Entry added to the blacklist. Edit the label to your liking. 条目添加到黑名单. 根据自己的喜好编辑标签. - + Error: You cannot add the same address to your blacklist twice. Try renaming the existing one if you want. 错误: 您不能在同一地址添加到您的黑名单两次. 也许您可重命名现有之一. - + Moved items to trash. 已经移动项目到回收站。 - + Undeleted item. 未删除的项目。 - + Save As... 另存为... - + Write error. 写入失败。 - + No addresses selected. 没有选择地址。 - + If you delete the subscription, messages that you already received will become inaccessible. Maybe you can consider disabling the subscription instead. Disabled subscriptions will not receive new messages, but you can still view messages you already received. Are you sure you want to delete the subscription? @@ -803,7 +803,7 @@ Are you sure you want to delete the subscription? 你确定要删除订阅? - + If you delete the channel, messages that you already received will become inaccessible. Maybe you can consider disabling the channel instead. Disabled channels will not receive new messages, but you can still view messages you already received. Are you sure you want to delete the channel? @@ -812,92 +812,92 @@ Are you sure you want to delete the channel? 你确定要删除频道? - + Do you really want to remove this avatar? 您真的想移除这个头像么? - + You have already set an avatar for this address. Do you really want to overwrite it? 您已经为这个地址设置了头像了。您真的想移除么? - + Start-on-login not yet supported on your OS. 登录时启动尚未支持您在使用的操作系统。 - + Minimize-to-tray not yet supported on your OS. 最小化到托盘尚未支持您的操作系统。 - + Tray notifications not yet supported on your OS. 托盘提醒尚未支持您所使用的操作系统。 - + Testing... 正在测试... - + This is a chan address. You cannot use it as a pseudo-mailing list. 这是一个频道地址,您无法把它作为伪邮件列表。 - + The address should start with ''BM-'' 地址应该以"BM-"开始 - + The address is not typed or copied correctly (the checksum failed). 地址没有被正确的键入或复制(校验码校验失败)。 - + The version number of this address is higher than this software can support. Please upgrade Bitmessage. 这个地址的版本号大于此软件的最大支持。 请升级比特信。 - + The address contains invalid characters. 这个地址中包含无效字符。 - + Some data encoded in the address is too short. 在这个地址中编码的部分信息过少。 - + Some data encoded in the address is too long. 在这个地址中编码的部分信息过长。 - + Some data encoded in the address is malformed. 在地址编码的某些数据格式不正确. - + Enter an address above. 请在上方键入地址。 - + Address is an old type. We cannot display its past broadcasts. 地址没有近期的广播。我们无法显示之间的广播。 - + There are no recent broadcasts from this address to display. 没有可以显示的近期广播。 - + You are using TCP port %1. (This can be changed in the settings). 您正在使用TCP端口 %1 。(可以在设置中修改)。 @@ -1107,47 +1107,47 @@ Are you sure you want to delete the channel? 添加新条目 - + Display the %1 recent broadcast(s) from this address. 显示从这个地址%1的最近广播 - + New version of PyBitmessage is available: %1. Download it from https://github.com/Bitmessage/PyBitmessage/releases/latest PyBitmessage的新版本可用: %1. 从https://github.com/Bitmessage/PyBitmessage/releases/latest下载 - + Waiting for PoW to finish... %1% 等待PoW完成...%1% - + Shutting down Pybitmessage... %1% 关闭Pybitmessage ...%1% - + Waiting for objects to be sent... %1% 等待要发送对象...%1% - + Saving settings... %1% 保存设置...%1% - + Shutting down core... %1% 关闭核心...%1% - + Stopping notifications... %1% 停止通知...%1% - + Shutdown imminent... %1% 关闭即将来临...%1% @@ -1157,17 +1157,17 @@ Are you sure you want to delete the channel? %n 小时 - + %n day(s) %n 天 - + Shutting down PyBitmessage... %1% 关闭PyBitmessage...%1% - + Sent 发送 @@ -1212,86 +1212,86 @@ Are you sure you want to delete the channel? 警告: 您的磁盘或数据存储量已满. 比特信将立即退出. - + Error! Could not find sender address (your address) in the keys.dat file. 错误! 找不到在keys.dat 件发件人的地址 ( 您的地址). - + Doing work necessary to send broadcast... 做必要的工作, 以发送广播... - + Broadcast sent on %1 广播发送%1 - + Encryption key was requested earlier. 加密密钥已请求. - + Sending a request for the recipient's encryption key. 发送收件人的加密密钥的请求. - + Looking up the receiver's public key 展望接收方的公钥 - + Problem: Destination is a mobile device who requests that the destination be included in the message but this is disallowed in your settings. %1 问题: 目标是移动电话设备所请求的目的地包括在消息中, 但是这是在你的设置禁止. %1 - + Doing work necessary to send message. There is no required difficulty for version 2 addresses like this. 做必要的工作, 以发送信息. 这样第2版的地址没有难度. - + Doing work necessary to send message. Receiver's required difficulty: %1 and %2 做必要的工作, 以发送短信. 接收者的要求难度: %1与%2 - + Problem: The work demanded by the recipient (%1 and %2) is more difficult than you are willing to do. %3 问题: 由接收者(%1%2)要求的工作量比您愿意做的工作量來得更困难. %3 - + Problem: You are trying to send a message to yourself or a chan but your encryption key could not be found in the keys.dat file. Could not encrypt message. %1 问题: 您正在尝试将信息发送给自己或频道, 但您的加密密钥无法在keys.dat文件中找到. 无法加密信息. %1 - + Doing work necessary to send message. 做必要的工作, 以发送信息. - + Message sent. Waiting for acknowledgement. Sent on %1 信息发送. 等待确认. 已发送%1 - + Doing work necessary to request encryption key. 做必要的工作以要求加密密钥. - + Broadcasting the public key request. This program will auto-retry if they are offline. 广播公钥请求. 这个程序将自动重试, 如果他们处于离线状态. - + Sending public key request. Waiting for reply. Requested at %1 发送公钥的请求. 等待回复. 请求在%1 @@ -1306,37 +1306,37 @@ Receiver's required difficulty: %1 and %2 UPnP端口映射被删除 - + Mark all messages as read 标记全部信息为已读 - + Are you sure you would like to mark all messages read? 确定将所有信息标记为已读吗? - + Doing work necessary to send broadcast. 持续进行必要的工作,以发送广播。 - + Proof of work pending 待传输内容的校验 - + %n object(s) pending proof of work %n 待传输内容校验任务 - + %n object(s) waiting to be distributed %n 任务等待分配 - + Wait until these tasks finish? 等待所有任务执行完? @@ -1396,12 +1396,17 @@ Receiver's required difficulty: %1 and %2 不能理解 NMControl。 - + Your GPU(s) did not calculate correctly, disabling OpenCL. Please report to the developers. 你的GPU(s)不能够正确计算,关闭OpenGL。请报告给开发者。 - + + Set notification sound... + 设置通知提示音... + + + Welcome to easy and secure Bitmessage * send messages to other people @@ -1415,85 +1420,110 @@ Receiver's required difficulty: %1 and %2 *在频道(s)里和其他人讨论 - + not recommended for chans 频道内不建议的内容 - + + Quiet Mode + 静默模式 + + + Problems connecting? Try enabling UPnP in the Network Settings 连接问题?请尝试在网络设置里打开UPnP - + + You are trying to send an email instead of a bitmessage. This requires registering with a gateway. Attempt to register? + 您将要尝试经由 Bitmessage 发送一封电子邮件。该操作需要在一个网关上注册。尝试注册? + + + Error: Bitmessage addresses start with BM- Please check the recipient address %1 错误:Bitmessage地址是以BM-开头的,请检查收信地址%1. - + Error: The recipient address %1 is not typed or copied correctly. Please check it. 错误:收信地址%1未填写或复制错误。请检查。 - + Error: The recipient address %1 contains invalid characters. Please check it. 错误:收信地址%1还有非法字符。请检查。 - + Error: The version of the recipient address %1 is too high. Either you need to upgrade your Bitmessage software or your acquaintance is being clever. 错误:收信地址%1版本太高。要么你需要更新你的软件,要么对方需要降级 。 - + Error: Some data encoded in the recipient address %1 is too short. There might be something wrong with the software of your acquaintance. 错误:收信地址%1编码数据太短。可能对方使用的软件有问题。 - + Error: Some data encoded in the recipient address %1 is too long. There might be something wrong with the software of your acquaintance. 错误: - + Error: Some data encoded in the recipient address %1 is malformed. There might be something wrong with the software of your acquaintance. 错误:收信地址%1编码数据太长。可能对方使用的软件有问题。 - + Error: Something is wrong with the recipient address %1. 错误:收信地址%1有问题。 - + + Error: %1 + 错误:%1 + + + + From %1 + 来自 %1 + + + Synchronisation pending 待同步 - + Bitmessage hasn't synchronised with the network, %n object(s) to be downloaded. If you quit now, it may cause delivery delays. Wait until the synchronisation finishes? Bitmessage还没有与网络同步,%n 件任务需要下载。如果你现在退出软件,可能会造成传输延时。是否等同步完成? - + Not connected 未连接成功。 - + Bitmessage isn't connected to the network. If you quit now, it may cause delivery delays. Wait until connected and the synchronisation finishes? Bitmessage未连接到网络。如果现在退出软件,可能会造成传输延时。是否等待同步完成? - + Waiting for network connection... 等待网络连接…… - + Waiting for finishing synchronisation... 等待同步完成…… + + + You have already set a notification sound for this address book entry. Do you really want to overwrite it? + + MessageView @@ -1836,37 +1866,37 @@ The 'Random Number' option is selected by default but deterministic ad 总连接: - + Since startup: 自启动: - + Processed 0 person-to-person messages. 处理0人对人的信息. - + Processed 0 public keys. 处理0公钥。 - + Processed 0 broadcasts. 处理0广播. - + Inventory lookups per second: 0 每秒库存查询: 0 - + Objects to be synced: 对象 已同步: - + Stream # 数据流 # @@ -1876,37 +1906,37 @@ The 'Random Number' option is selected by default but deterministic ad - + Since startup on %1 自从%1启动 - + Down: %1/s Total: %2 下: %1/秒 总计: %2 - + Up: %1/s Total: %2 上: %1/秒 总计: %2 - + Total Connections: %1 总的连接数: %1 - + Inventory lookups per second: %1 每秒库存查询: %1 - + Up: 0 kB/s 上载: 0 kB /秒 - + Down: 0 kB/s 下载: 0 kB /秒 @@ -1916,72 +1946,72 @@ The 'Random Number' option is selected by default but deterministic ad 网络状态 - + byte(s) 字节 - + Object(s) to be synced: %n 要同步的对象: %n - + Processed %n person-to-person message(s). 处理%n人对人的信息. - + Processed %n broadcast message(s). 处理%n广播信息. - + Processed %n public key(s). 处理%n公钥. - + Peer 节点 - + IP address or hostname IP地址或主机名 - + Rating - 等级 + 评分 - + PyBitmessage tracks the success rate of connection attempts to individual nodes. The rating ranges from -1 to 1 and affects the likelihood of selecting the node in the future PyBitmessage 跟踪连接尝试到各个节点的成功率。评分范围从 -1 到 1 ,这会影响到未来选择节点的可能性。 - + User agent 用户代理 - + Peer's self-reported software 节点自我报告的软件 - + TLS TLS - + Connection encryption 连接加密 - + List of streams negotiated between you and the peer 您和节点之间已协商的流列表 @@ -2040,8 +2070,8 @@ The 'Random Number' option is selected by default but deterministic ad - Chan passhphrase/name: - 频道命名: + Chan passphrase/name: + @@ -2062,17 +2092,17 @@ The 'Random Number' option is selected by default but deterministic ad newchandialog - + Successfully created / joined chan %1 成功创建或加入频道%1 - + Chan creation / joining failed 频道创建或加入失败 - + Chan creation / joining cancelled 频道创建或加入已取消 @@ -2080,17 +2110,17 @@ The 'Random Number' option is selected by default but deterministic ad proofofwork - + C PoW module built successfully. C PoW模块编译成功。 - + Failed to build C PoW module. Please build it manually. 无法编译C PoW模块。请手动编译。 - + C PoW module unavailable. Please build it. C PoW模块不可用。请编译它。 From 89567cecfa7246b717141955eb438540cebfb41e Mon Sep 17 00:00:00 2001 From: Peter Surda Date: Tue, 14 Nov 2017 23:19:16 +0100 Subject: [PATCH 272/407] Notifier plugin fix - NotifyOSD doesn't like too many notification objects in a queue, so just create one on init and update its contents if there is a new notification --- src/plugins/notification_notify2.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/plugins/notification_notify2.py b/src/plugins/notification_notify2.py index 90f09df3..e91d8f1b 100644 --- a/src/plugins/notification_notify2.py +++ b/src/plugins/notification_notify2.py @@ -6,8 +6,10 @@ from gi.repository import Notify Notify.init('pybitmessage') - def connect_plugin(title, subtitle, category, label, icon): if not icon: icon = 'mail-message-new' if category == 2 else 'pybitmessage' - Notify.Notification.new(title, subtitle, icon).show() + connect_plugin.notification.update(title, subtitle, icon) + connect_plugin.notification.show.show() + +connect_plugin.notification = Notify.Notification.new("Init", "Init") From d2f79d3172ff395084ecc2e4ba5c61005bb06662 Mon Sep 17 00:00:00 2001 From: Peter Surda Date: Tue, 14 Nov 2017 23:20:15 +0100 Subject: [PATCH 273/407] sqlite storage fix - typo on cleaning --- src/storage/sqlite.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/storage/sqlite.py b/src/storage/sqlite.py index b5c48e3b..aba64244 100644 --- a/src/storage/sqlite.py +++ b/src/storage/sqlite.py @@ -75,6 +75,6 @@ class SqliteInventory(InventoryStorage): with self.lock: sqlExecute('DELETE FROM inventory WHERE expirestime Date: Tue, 14 Nov 2017 23:43:05 +0100 Subject: [PATCH 274/407] close handling fix - don't close a connection twice --- src/network/connectionpool.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/network/connectionpool.py b/src/network/connectionpool.py index 04336b00..ebde717b 100644 --- a/src/network/connectionpool.py +++ b/src/network/connectionpool.py @@ -236,7 +236,7 @@ class BMConnectionPool(object): i.append_write_buf(protocol.CreatePacket('ping')) else: i.close_reason = "Timeout (%is)" % (time.time() - i.lastTx) - i.handle_close() + i.set_state("close") for i in self.inboundConnections.values() + self.outboundConnections.values() + self.listeningSockets.values() + self.udpSockets.values(): if i.state == "close": i.handle_close() From 2da1115d17e22bac2dcc60e4fb74241b4df9ea76 Mon Sep 17 00:00:00 2001 From: Peter Surda Date: Tue, 14 Nov 2017 23:46:07 +0100 Subject: [PATCH 275/407] Typo Typo in 89567cecfa7246b717141955eb438540cebfb41e --- src/plugins/notification_notify2.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/plugins/notification_notify2.py b/src/plugins/notification_notify2.py index e91d8f1b..3fd935c4 100644 --- a/src/plugins/notification_notify2.py +++ b/src/plugins/notification_notify2.py @@ -10,6 +10,6 @@ def connect_plugin(title, subtitle, category, label, icon): if not icon: icon = 'mail-message-new' if category == 2 else 'pybitmessage' connect_plugin.notification.update(title, subtitle, icon) - connect_plugin.notification.show.show() + connect_plugin.notification.show() connect_plugin.notification = Notify.Notification.new("Init", "Init") From 5a787f41d2527e936f2f6d2ae3da9e7f177a66cb Mon Sep 17 00:00:00 2001 From: Peter Surda Date: Fri, 17 Nov 2017 13:37:51 +0100 Subject: [PATCH 276/407] Socket closing changes - explicit close only through asyncore error handler - implicit close through garbage collector - avoid duplicate closing --- src/network/advanceddispatcher.py | 6 +++--- src/network/bmproto.py | 3 +++ src/network/connectionpool.py | 8 ++++---- 3 files changed, 10 insertions(+), 7 deletions(-) diff --git a/src/network/advanceddispatcher.py b/src/network/advanceddispatcher.py index 50ddf44b..c50b6a43 100644 --- a/src/network/advanceddispatcher.py +++ b/src/network/advanceddispatcher.py @@ -78,7 +78,7 @@ class AdvancedDispatcher(asyncore.dispatcher): self.uploadChunk = asyncore.uploadBucket self.uploadChunk = min(self.uploadChunk, len(self.write_buf)) return asyncore.dispatcher.writable(self) and \ - (self.connecting or self.uploadChunk > 0) + (self.connecting or (self.connected and self.uploadChunk > 0)) def readable(self): self.downloadChunk = AdvancedDispatcher._buf_len @@ -92,7 +92,7 @@ class AdvancedDispatcher(asyncore.dispatcher): except AttributeError: pass return asyncore.dispatcher.readable(self) and \ - (self.connecting or self.downloadChunk > 0) + (self.connecting or self.accepting or (self.connected and self.downloadChunk > 0)) def handle_read(self): self.lastTx = time.time() @@ -127,5 +127,5 @@ class AdvancedDispatcher(asyncore.dispatcher): self.read_buf = bytearray() with self.writeLock: self.write_buf = bytearray() - self.state = "close" + self.set_state("close") self.close() diff --git a/src/network/bmproto.py b/src/network/bmproto.py index 9bc60af7..e4f7b406 100644 --- a/src/network/bmproto.py +++ b/src/network/bmproto.py @@ -546,6 +546,9 @@ class BMProto(AdvancedDispatcher, ObjectTracker): def handle_close(self): self.set_state("close") + if not (self.accepting or self.connecting or self.connected): + # already disconnected + return try: logger.debug("%s:%i: closing, %s", self.destination.host, self.destination.port, self.close_reason) except AttributeError: diff --git a/src/network/connectionpool.py b/src/network/connectionpool.py index ebde717b..aa48093e 100644 --- a/src/network/connectionpool.py +++ b/src/network/connectionpool.py @@ -214,11 +214,13 @@ class BMConnectionPool(object): else: if self.listeningSockets: for i in self.listeningSockets.values(): - i.handle_close() + i.close_reason = "Stopping listening" + i.accepting = i.connecting = i.connected = False logger.info('Stopped listening for incoming connections.') if self.udpSockets: for i in self.udpSockets.values(): - i.handle_close() + i.close_reason = "Stopping UDP socket" + i.accepting = i.connecting = i.connected = False logger.info('Stopped udp sockets.') loopTime = float(self.spawnWait) @@ -238,8 +240,6 @@ class BMConnectionPool(object): i.close_reason = "Timeout (%is)" % (time.time() - i.lastTx) i.set_state("close") for i in self.inboundConnections.values() + self.outboundConnections.values() + self.listeningSockets.values() + self.udpSockets.values(): - if i.state == "close": - i.handle_close() if not (i.accepting or i.connecting or i.connected): reaper.append(i) for i in reaper: From 1e02d2b48a5e135f0eb8567c4b374b6c84205a97 Mon Sep 17 00:00:00 2001 From: Peter Surda Date: Fri, 17 Nov 2017 19:49:51 +0100 Subject: [PATCH 277/407] Download optimisation - pending download tracking now per-connection instead of globally --- src/network/downloadthread.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/network/downloadthread.py b/src/network/downloadthread.py index 414aacd2..8c384edc 100644 --- a/src/network/downloadthread.py +++ b/src/network/downloadthread.py @@ -48,7 +48,7 @@ class DownloadThread(threading.Thread, StoppableThread): # this may take a while, but it needs a consistency so I think it's better to lock a bigger chunk with i.objectsNewToMeLock: try: - downloadPending = len(list((k for k, v in i.objectsNewToMe.iteritems() if k in missingObjects and missingObjects[k] > timedOut))) + downloadPending = len(list((k for k, v in i.objectsNewToMe.iteritems() if k in missingObjects and missingObjects[k] > timedOut and not v))) except KeyError: continue if downloadPending >= DownloadThread.minPending: From 3c3d69e5de70dbc01f3dfd6063d2775e89fdad12 Mon Sep 17 00:00:00 2001 From: Peter Surda Date: Fri, 17 Nov 2017 19:50:39 +0100 Subject: [PATCH 278/407] Reap closed connection fix --- src/network/connectionpool.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/network/connectionpool.py b/src/network/connectionpool.py index aa48093e..d0cbdcd0 100644 --- a/src/network/connectionpool.py +++ b/src/network/connectionpool.py @@ -242,5 +242,11 @@ class BMConnectionPool(object): for i in self.inboundConnections.values() + self.outboundConnections.values() + self.listeningSockets.values() + self.udpSockets.values(): if not (i.accepting or i.connecting or i.connected): reaper.append(i) + else: + try: + if i.state == "close": + reaper.append(i) + except AttributeError: + pass for i in reaper: self.removeConnection(i) From 4690dd6f00c3f26f3ab17138c0e50231f2dfc83b Mon Sep 17 00:00:00 2001 From: Peter Surda Date: Fri, 17 Nov 2017 23:53:46 +0100 Subject: [PATCH 279/407] Copy object contents from buffers on instantiation - this may fix some memory issues --- src/network/bmobject.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/network/bmobject.py b/src/network/bmobject.py index f4c883ca..249ec2ab 100644 --- a/src/network/bmobject.py +++ b/src/network/bmobject.py @@ -40,8 +40,9 @@ class BMObject(object): self.version = version self.streamNumber = streamNumber self.inventoryHash = calculateInventoryHash(data) - self.data = data - self.tag = data[payloadOffset:payloadOffset+32] + # copy to avoid memory issues + self.data = bytearray(data) + self.tag = self.data[payloadOffset:payloadOffset+32] def checkProofOfWorkSufficient(self): # Let us check to make sure that the proof of work is sufficient. From 3aa6f386dbb0099b99babf150893ba4ac40d1996 Mon Sep 17 00:00:00 2001 From: Peter Surda Date: Sat, 18 Nov 2017 09:47:17 +0100 Subject: [PATCH 280/407] Dandelion fixes - dandelion would always think there is a cycle and trigger fluff - cycle fluff trigger didn't correctly re-download and re-announce the object. Now it remembers between (d)inv and object commands that it's in a fluff trigger phase. --- src/network/bmproto.py | 2 +- src/network/connectionpool.py | 2 ++ src/network/dandelion.py | 9 +++++++++ src/network/objectracker.py | 10 ++++------ 4 files changed, 16 insertions(+), 7 deletions(-) diff --git a/src/network/bmproto.py b/src/network/bmproto.py index e4f7b406..17b2c761 100644 --- a/src/network/bmproto.py +++ b/src/network/bmproto.py @@ -326,8 +326,8 @@ class BMProto(AdvancedDispatcher, ObjectTracker): return True for i in map(str, items): - Dandelion().addHash(i, self) self.handleReceivedInventory(i) + Dandelion().addHash(i, self) return True diff --git a/src/network/connectionpool.py b/src/network/connectionpool.py index d0cbdcd0..46bb8aba 100644 --- a/src/network/connectionpool.py +++ b/src/network/connectionpool.py @@ -51,6 +51,8 @@ class BMConnectionPool(object): del i.objectsNewToThem[hashid] except KeyError: pass + if hashid in Dandelion().fluff: + Dandelion.removeHash(hashid) def reRandomiseDandelionStems(self): # Choose 2 peers randomly diff --git a/src/network/dandelion.py b/src/network/dandelion.py index a7ef4083..3e49d906 100644 --- a/src/network/dandelion.py +++ b/src/network/dandelion.py @@ -16,6 +16,7 @@ class Dandelion(): self.stem = [] self.nodeMap = {} self.hashMap = {} + self.fluff = {} self.timeout = {} self.refresh = time() + REASSIGN_INTERVAL self.lock = RLock() @@ -37,6 +38,14 @@ class Dandelion(): del self.timeout[hashId] except KeyError: pass + try: + del self.fluff[hashId] + except KeyError: + pass + + def fluffTrigger(self, hashId): + with self.lock: + self.fluff[hashId] = None def maybeAddStem(self, connection): # fewer than MAX_STEMS outbound connections at last reshuffle? diff --git a/src/network/objectracker.py b/src/network/objectracker.py index a86ec23f..f846e7d5 100644 --- a/src/network/objectracker.py +++ b/src/network/objectracker.py @@ -82,16 +82,14 @@ class ObjectTracker(object): del self.objectsNewToThem[hashId] except KeyError: pass - if hashId not in Inventory(): + # Fluff trigger by cycle detection + if hashId not in Inventory() or hashId in Dandelion().hashMap: + if hashId in Dandelion().hashMap: + Dandelion().fluffTrigger(hashId) if hashId not in missingObjects: missingObjects[hashId] = time.time() with self.objectsNewToMeLock: self.objectsNewToMe[hashId] = True - elif hashId in Dandelion().hashMap: - # Fluff trigger by cycle detection - Dandelion().removeHash(hashId) - with self.objectsNewToMeLock: - self.objectsNewToMe[hashId] = True def hasAddr(self, addr): if haveBloom: From 5e042b76e70f7b262b083447c05a87fa8da0ed85 Mon Sep 17 00:00:00 2001 From: Peter Surda Date: Sat, 18 Nov 2017 10:05:41 +0100 Subject: [PATCH 281/407] Typo - missing brackets --- src/network/connectionpool.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/network/connectionpool.py b/src/network/connectionpool.py index 46bb8aba..f489d9fc 100644 --- a/src/network/connectionpool.py +++ b/src/network/connectionpool.py @@ -52,7 +52,7 @@ class BMConnectionPool(object): except KeyError: pass if hashid in Dandelion().fluff: - Dandelion.removeHash(hashid) + Dandelion().removeHash(hashid) def reRandomiseDandelionStems(self): # Choose 2 peers randomly From fdfbb77ed204077bd4560e173e4bf97487e55d6f Mon Sep 17 00:00:00 2001 From: Peter Surda Date: Sun, 19 Nov 2017 00:05:55 +0100 Subject: [PATCH 282/407] Close filehandles on connection reaping - I thought this is done automatically through garbage collection, but I think as the channel is still assigned in the asyncore map, it needs to be done manually. Basically filehandle limit exceeded and it crashed --- src/network/connectionpool.py | 1 + 1 file changed, 1 insertion(+) diff --git a/src/network/connectionpool.py b/src/network/connectionpool.py index f489d9fc..2f937a15 100644 --- a/src/network/connectionpool.py +++ b/src/network/connectionpool.py @@ -111,6 +111,7 @@ class BMConnectionPool(object): del self.inboundConnections[connection.destination.host] except KeyError: pass + connection.close() def getListeningIP(self): if BMConfigParser().safeGet("bitmessagesettings", "onionhostname").endswith(".onion"): From e558b1fb72e6a3d95f4d51ea219dc25e15924510 Mon Sep 17 00:00:00 2001 From: Peter Surda Date: Sun, 19 Nov 2017 13:48:43 +0100 Subject: [PATCH 283/407] Error handling - proxy connections didn't init fullyEstablished --- src/network/proxy.py | 1 + 1 file changed, 1 insertion(+) diff --git a/src/network/proxy.py b/src/network/proxy.py index c131c22a..7d46cd86 100644 --- a/src/network/proxy.py +++ b/src/network/proxy.py @@ -64,6 +64,7 @@ class Proxy(AdvancedDispatcher): AdvancedDispatcher.__init__(self) self.destination = address self.isOutbound = True + self.fullyEstablished = False self.create_socket(socket.AF_INET, socket.SOCK_STREAM) self.connect(self.proxy) From 6ca3460090dfe09b2d184f21f5c48f7fdb272d6f Mon Sep 17 00:00:00 2001 From: Peter Surda Date: Wed, 22 Nov 2017 14:49:18 +0100 Subject: [PATCH 284/407] Put garbage collection into the cleaner thread - this maybe addresses #1079 --- src/class_singleCleaner.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/class_singleCleaner.py b/src/class_singleCleaner.py index f3125806..e89dd1b1 100644 --- a/src/class_singleCleaner.py +++ b/src/class_singleCleaner.py @@ -1,7 +1,7 @@ +import gc import threading import shared import time -import sys import os import tr#anslate @@ -14,7 +14,6 @@ from network.dandelion import Dandelion from debug import logger import knownnodes import queues -import protocol import state """ @@ -46,6 +45,7 @@ class singleCleaner(threading.Thread, StoppableThread): self.initStop() def run(self): + gc.disable() timeWeLastClearedInventoryAndPubkeysTables = 0 try: shared.maximumLengthOfTimeToBotherResendingMessages = (float(BMConfigParser().get('bitmessagesettings', 'stopresendingafterxdays')) * 24 * 60 * 60) + (float(BMConfigParser().get('bitmessagesettings', 'stopresendingafterxmonths')) * (60 * 60 * 24 *365)/12) @@ -148,6 +148,8 @@ class singleCleaner(threading.Thread, StoppableThread): pass # TODO: cleanup pending upload / download + gc.collect() + if state.shutdown == 0: self.stop.wait(singleCleaner.cycleLength) From 44dd08a2284a5300887ae012f754dfa0d64689b8 Mon Sep 17 00:00:00 2001 From: Peter Surda Date: Wed, 22 Nov 2017 21:13:35 +0100 Subject: [PATCH 285/407] Remove obsolete code in cleaner thread --- src/class_singleCleaner.py | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/src/class_singleCleaner.py b/src/class_singleCleaner.py index e89dd1b1..bec1a11d 100644 --- a/src/class_singleCleaner.py +++ b/src/class_singleCleaner.py @@ -63,8 +63,6 @@ class singleCleaner(threading.Thread, StoppableThread): Inventory().flush() queues.UISignalQueue.put(('updateStatusBar', '')) - protocol.broadcastToSendDataQueues(( - 0, 'pong', 'no data')) # commands the sendData threads to send out a pong message if they haven't sent anything else in the last five minutes. The socket timeout-time is 10 minutes. # If we are running as a daemon then we are going to fill up the UI # queue which will never be handled by a UI. We should clear it to # save memory. @@ -127,10 +125,10 @@ class singleCleaner(threading.Thread, StoppableThread): os._exit(0) shared.needToWriteKnownNodesToDisk = False - # clear download queues - for thread in threading.enumerate(): - if thread.isAlive() and hasattr(thread, 'downloadQueue'): - thread.downloadQueue.clear() +# # clear download queues +# for thread in threading.enumerate(): +# if thread.isAlive() and hasattr(thread, 'downloadQueue'): +# thread.downloadQueue.clear() # inv/object tracking for connection in BMConnectionPool().inboundConnections.values() + BMConnectionPool().outboundConnections.values(): From 52162046bd51b472597f7a32a50b5cb3f1040d31 Mon Sep 17 00:00:00 2001 From: PyBitmessage Translations Date: Wed, 29 Nov 2017 10:38:58 +0100 Subject: [PATCH 286/407] Auto-updated language ru from transifex --- src/translations/bitmessage_ru.qm | Bin 93991 -> 94127 bytes src/translations/bitmessage_ru.ts | 342 +++++++++++++++--------------- 2 files changed, 176 insertions(+), 166 deletions(-) diff --git a/src/translations/bitmessage_ru.qm b/src/translations/bitmessage_ru.qm index 2cdfe4db1dcb94bd4196bf9f57bcc9f46dcc9db9..82604f65804e3eb917008e1b63b0678f848e6585 100644 GIT binary patch delta 3508 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 EmailGatewayRegistrationDialog - + Registration failed: Регистрация не удалась: - + The requested email address is not available, please try a new one. Fill out the new desired email address (including @mailchuck.com) below: Запрашиваемый адрес email недоступен, попробуйте ввести другой. Введите желаемый адрес (включая @mailchuck.com) ниже: @@ -283,7 +283,7 @@ Please type the desired email address (including @mailchuck.com) below: Один из Ваших адресов, %1, является устаревшим адресом версии 1. Адреса версии 1 больше не поддерживаются. Хотите ли Вы удалить его сейчас? - + Waiting for their encryption key. Will request it again soon. Ожидаем ключ шифрования от Вашего собеседника. Запрос будет повторен через некоторое время. @@ -293,17 +293,17 @@ Please type the desired email address (including @mailchuck.com) below: - + Queued. В очереди. - + Message sent. Waiting for acknowledgement. Sent at %1 Сообщение отправлено. Ожидаем подтверждения. Отправлено в %1 - + Message sent. Sent at %1 Сообщение отправлено в %1 @@ -313,47 +313,47 @@ Please type the desired email address (including @mailchuck.com) below: - + Acknowledgement of the message received %1 Доставлено в %1 - + Broadcast queued. Рассылка ожидает очереди. - + Broadcast on %1 Рассылка на %1 - + Problem: The work demanded by the recipient is more difficult than you are willing to do. %1 Проблема: Ваш получатель требует более сложных вычислений, чем максимум, указанный в Ваших настройках. %1 - + Problem: The recipient's encryption key is no good. Could not encrypt message. %1 Проблема: ключ получателя неправильный. Невозможно зашифровать сообщение. %1 - + Forced difficulty override. Send should start soon. Форсирована смена сложности. Отправляем через некоторое время. - + Unknown status: %1 %2 Неизвестный статус: %1 %2 - + Not Connected Не соединено - + Show Bitmessage Показать Bitmessage @@ -363,12 +363,12 @@ Please type the desired email address (including @mailchuck.com) below: Отправить - + Subscribe Подписки - + Channel Канал @@ -378,13 +378,13 @@ Please type the desired email address (including @mailchuck.com) below: Выйти - + You may manage your keys by editing the keys.dat file stored in the same directory as this program. It is important that you back up this file. Вы можете управлять Вашими ключами, редактируя файл keys.dat, находящийся в той же папке, что и эта программа. Создайте резервную копию этого файла перед тем как будете его редактировать. - + You may manage your keys by editing the keys.dat file stored in %1 It is important that you back up this file. @@ -393,19 +393,19 @@ It is important that you back up this file. Создайте резервную копию этого файла перед тем как будете его редактировать. - + Open keys.dat? Открыть файл keys.dat? - + You may manage your keys by editing the keys.dat file stored in the same directory as this program. It is important that you back up this file. Would you like to open the file now? (Be sure to close Bitmessage before making any changes.) Вы можете управлять Вашими ключами, редактируя файл keys.dat, находящийся в той же папке, что и эта программа. Создайте резервную копию этого файла перед тем как будете его редактировать. Хотели бы Вы открыть этот файл сейчас? (пожалуйста, закройте Bitmessage до того как Вы внесёте в этот файл какие-либо изменения.) - + You may manage your keys by editing the keys.dat file stored in %1 It is important that you back up this file. Would you like to open the file now? (Be sure to close Bitmessage before making any changes.) @@ -415,37 +415,37 @@ It is important that you back up this file. Would you like to open the file now? (пожалуйста, закройте Bitmessage до того как Вы внесёте в этот файл какие-либо изменения.) - + Delete trash? Очистить корзину? - + Are you sure you want to delete all trashed messages? Вы уверены что хотите очистить корзину? - + bad passphrase Неподходящая секретная фраза - + You must type your passphrase. If you don't have one then this is not the form for you. Вы должны ввести секретную фразу. Если Вы не хотите этого делать, то Вы выбрали неправильную опцию. - + Bad address version number Неверный номер версии адреса - + Your address version number must be a number: either 3 or 4. Адрес номера версии должен быть числом: либо 3, либо 4. - + Your address version number must be either 3 or 4. Адрес номера версии должен быть либо 3, либо 4. @@ -515,22 +515,22 @@ It is important that you back up this file. Would you like to open the file now? - + Connection lost Соединение потеряно - + Connected Соединено - + Message trashed Сообщение удалено - + The TTL, or Time-To-Live is the length of time that the network will hold the message. The recipient must get it during this time. If your Bitmessage client does not hear an acknowledgement, it will resend the message automatically. The longer the Time-To-Live, the @@ -541,17 +541,17 @@ It is important that you back up this file. Would you like to open the file now? сообщение. Часто разумным вариантом будет установка TTL на 4 или 5 дней. - + Message too long Сообщение слишком длинное - + The message that you are trying to send is too long by %1 bytes. (The maximum is 261644 bytes). Please cut it down before sending. Сообщение, которое вы пытаетесь отправить, длиннее максимально допустимого на %1 байт. (Максимально допустимое значение 261644 байта). Пожалуйста, сократите сообщение перед отправкой. - + Error: Your account wasn't registered at an email gateway. Sending registration now as %1, please wait for the registration to be processed before retrying sending. Ошибка: ваш аккаунт не зарегистрирован на Email-шлюзе. Отправка регистрации %1, пожалуйста, подождите пока процесс регистрации не завершится, прежде чем попытаться отправить сообщение заново. @@ -596,57 +596,57 @@ It is important that you back up this file. Would you like to open the file now? - + Error: You must specify a From address. If you don't have one, go to the 'Your Identities' tab. Вы должны указать адрес в поле "От кого". Вы можете найти Ваш адрес во вкладке "Ваши Адреса". - + Address version number Версия адреса - + Concerning the address %1, Bitmessage cannot understand address version numbers of %2. Perhaps upgrade Bitmessage to the latest version. По поводу адреса %1: Bitmessage не поддерживаем адреса версии %2. Возможно, Вам нужно обновить клиент Bitmessage. - + Stream number Номер потока - + Concerning the address %1, Bitmessage cannot handle stream numbers of %2. Perhaps upgrade Bitmessage to the latest version. По поводу адреса %1: Bitmessage не поддерживаем стрим номер %2. Возможно, Вам нужно обновить клиент Bitmessage. - + Warning: You are currently not connected. Bitmessage will do the work necessary to send the message but it won't send until you connect. Внимание: Вы не подключены к сети. Bitmessage выполнит работу, требуемую для отправки сообщения, но не отправит его до тех пор, пока Вы не подключитесь. - + Message queued. Сообщение в очереди. - + Your 'To' field is empty. Вы не заполнили поле 'Кому'. - + Right click one or more entries in your address book and select 'Send message to this address'. Нажмите правую кнопку мыши на каком-либо адресе и выберите "Отправить сообщение на этот адрес". - + Fetched address from namecoin identity. Получить адрес через Namecoin. - + New Message Новое сообщение @@ -656,7 +656,7 @@ It is important that you back up this file. Would you like to open the file now? - + Sending email gateway registration request Отправка запроса на регистрацию на Email-шлюзе @@ -671,142 +671,142 @@ It is important that you back up this file. Would you like to open the file now? Вы ввели неправильный адрес. Это адрес проигнорирован. - + Error: You cannot add the same address to your address book twice. Try renaming the existing one if you want. Ошибка: Вы не можете добавлять один и тот же адрес в Адресную Книгу несколько раз. Попробуйте переименовать существующий адрес. - + Error: You cannot add the same address to your subscriptions twice. Perhaps rename the existing one if you want. Ошибка: вы не можете добавить один и тот же адрес в ваши подписки дважды. Пожалуйста, переименуйте имеющийся адрес, если хотите. - + Restart Перезапустить - + You must restart Bitmessage for the port number change to take effect. Вы должны перезапустить Bitmessage, чтобы смена номера порта имела эффект. - + Bitmessage will use your proxy from now on but you may want to manually restart Bitmessage now to close existing connections (if any). Bitmessage будет использовать Ваш прокси, начиная прямо сейчас. Тем не менее Вам имеет смысл перезапустить Bitmessage, чтобы закрыть уже существующие соединения. - + Number needed Требуется число - + Your maximum download and upload rate must be numbers. Ignoring what you typed. Скорости загрузки и выгрузки должны быть числами. Игнорирую то, что вы набрали. - + Will not resend ever Не пересылать никогда - + Note that the time limit you entered is less than the amount of time Bitmessage waits for the first resend attempt therefore your messages will never be resent. Обратите внимание, что лимит времени, который вы ввели, меньше чем время, которое Bitmessage ждет перед первой попыткой переотправки сообщения, поэтому ваши сообщения никогда не будут переотправлены. - + Sending email gateway unregistration request Отправка запроса на отмену регистрации на Email-шлюзе - + Sending email gateway status request Отправка запроса статуса аккаунта на Email-шлюзе - + Passphrase mismatch Секретная фраза не подходит - + The passphrase you entered twice doesn't match. Try again. Вы ввели две разные секретные фразы. Пожалуйста, повторите заново. - + Choose a passphrase Придумайте секретную фразу - + You really do need a passphrase. Вы действительно должны ввести секретную фразу. - + Address is gone Адрес утерян - + Bitmessage cannot find your address %1. Perhaps you removed it? Bitmessage не может найти Ваш адрес %1. Возможно Вы удалили его? - + Address disabled Адрес выключен - + Error: The address from which you are trying to send is disabled. You'll have to enable it on the 'Your Identities' tab before using it. Ошибка: адрес, с которого Вы пытаетесь отправить, выключен. Вам нужно будет включить этот адрес во вкладке "Ваши адреса". - + Entry added to the Address Book. Edit the label to your liking. Запись добавлена в Адресную Книгу. Вы можете её отредактировать. - + Entry added to the blacklist. Edit the label to your liking. Запись добавлена в чёрный список. Измените название по своему вкусу. - + Error: You cannot add the same address to your blacklist twice. Try renaming the existing one if you want. Ошибка: вы не можете добавить один и тот же адрес в чёрный список дважды. Попробуйте переименовать существующий адрес. - + Moved items to trash. Удалено в корзину. - + Undeleted item. Отменить удаление записи - + Save As... Сохранить как ... - + Write error. Ошибка записи. - + No addresses selected. Вы не выбрали адрес. - + If you delete the subscription, messages that you already received will become inaccessible. Maybe you can consider disabling the subscription instead. Disabled subscriptions will not receive new messages, but you can still view messages you already received. Are you sure you want to delete the subscription? @@ -815,7 +815,7 @@ Are you sure you want to delete the subscription? Вы уверены, что хотите отменить подписку? - + If you delete the channel, messages that you already received will become inaccessible. Maybe you can consider disabling the channel instead. Disabled channels will not receive new messages, but you can still view messages you already received. Are you sure you want to delete the channel? @@ -824,92 +824,92 @@ Are you sure you want to delete the channel? Вы уверены, что хотите удалить канал? - + Do you really want to remove this avatar? Вы уверены, что хотите удалить этот аватар? - + You have already set an avatar for this address. Do you really want to overwrite it? У вас уже есть аватар для этого адреса. Вы уверены, что хотите перезаписать аватар? - + Start-on-login not yet supported on your OS. Запуск программы при входе в систему ещё не поддерживается в вашей операционной системе. - + Minimize-to-tray not yet supported on your OS. Сворачивание в трей ещё не поддерживается в вашей операционной системе. - + Tray notifications not yet supported on your OS. Уведомления в трее ещё не поддерживаеются в вашей операционной системе. - + Testing... Проверяем... - + This is a chan address. You cannot use it as a pseudo-mailing list. Это адрес chan-а. Вы не можете его использовать как адрес рассылки. - + The address should start with ''BM-'' Адрес должен начинаться с "BM-" - + The address is not typed or copied correctly (the checksum failed). Адрес введен или скопирован неверно (контрольная сумма не сходится). - + The version number of this address is higher than this software can support. Please upgrade Bitmessage. Версия этого адреса более поздняя, чем Ваша программа. Пожалуйста, обновите программу Bitmessage. - + The address contains invalid characters. Адрес содержит запрещённые символы. - + Some data encoded in the address is too short. Данные, закодированные в адресе, слишком короткие. - + Some data encoded in the address is too long. Данные, закодированные в адресе, слишком длинные. - + Some data encoded in the address is malformed. Данные, закодированные в адресе, имеют неверный формат. - + Enter an address above. Введите адрес выше. - + Address is an old type. We cannot display its past broadcasts. Адрес старого типа. Мы не можем отобразить его прошлые рассылки. - + There are no recent broadcasts from this address to display. Нет недавних рассылок с этого адреса для отображения. - + You are using TCP port %1. (This can be changed in the settings). Вы используете TCP порт %1. (Его можно поменять в настройках). @@ -1119,47 +1119,47 @@ Are you sure you want to delete the channel? Добавить новую запись - + Display the %1 recent broadcast(s) from this address. Показать %1 прошлых рассылок с этого адреса. - + New version of PyBitmessage is available: %1. Download it from https://github.com/Bitmessage/PyBitmessage/releases/latest Доступна новая версия PyBitmessage: %1. Загрузите её: https://github.com/Bitmessage/PyBitmessage/releases/latest - + Waiting for PoW to finish... %1% Ожидание окончания PoW... %1% - + Shutting down Pybitmessage... %1% Завершение PyBitmessage... %1% - + Waiting for objects to be sent... %1% Ожидание отправки объектов... %1% - + Saving settings... %1% Сохранение настроек... %1% - + Shutting down core... %1% Завершение работы ядра... %1% - + Stopping notifications... %1% Остановка сервиса уведомлений... %1% - + Shutdown imminent... %1% Завершение вот-вот произойдет... %1% @@ -1174,12 +1174,12 @@ Are you sure you want to delete the channel? %n день%n дня%n дней%n дней - + Shutting down PyBitmessage... %1% Завершение PyBitmessage... %1% - + Sent Отправлено @@ -1283,7 +1283,7 @@ Receiver's required difficulty: %1 and %2 Проблема: вы пытаетесь отправить сообщение самому себе или в чан, но ваш ключ шифрования не найден в файле ключей keys.dat. Невозможно зашифровать сообщение. %1 - + Doing work necessary to send message. Выполнение работы, требуемой для отправки сообщения. @@ -1293,7 +1293,7 @@ Receiver's required difficulty: %1 and %2 Отправлено. Ожидаем подтверждения. Отправлено в %1 - + Doing work necessary to request encryption key. Выполнение работы, требуемой для запроса ключа шифрования. @@ -1323,32 +1323,32 @@ Receiver's required difficulty: %1 and %2 Отметить все сообщения как прочтенные - + Are you sure you would like to mark all messages read? Вы уверены, что хотите отметить все сообщения как прочтенные? - + Doing work necessary to send broadcast. Выполнение работы, требуемой для отправки рассылки. - + Proof of work pending Ожидается доказательство работы - + %n object(s) pending proof of work %n объект в ожидании доказательства работы%n объекта в ожидании доказательства работы%n объектов в ожидании доказательства работы%n объектов в ожидании доказательства работы - + %n object(s) waiting to be distributed %n объект ожидает раздачи%n объекта ожидают раздачи%n объектов ожидают раздачи%n объектов ожидают раздачи - + Wait until these tasks finish? Подождать завершения этих задач? @@ -1413,7 +1413,7 @@ Receiver's required difficulty: %1 and %2 Ваша видеокарта вычислила неправильно, отключаем OpenCL. Пожалуйста, сообщите разработчикам. - + Set notification sound... Установить звук уведомления... @@ -1437,92 +1437,102 @@ Receiver's required difficulty: %1 and %2 не рекомендовано для чанов - + + Quiet Mode + Тихий режим + + + Problems connecting? Try enabling UPnP in the Network Settings Проблемы подключения? Попробуйте включить UPnP в сетевых настройках. - + You are trying to send an email instead of a bitmessage. This requires registering with a gateway. Attempt to register? Вы пытаетесь отправить email вместо bitmessage. Для этого нужно зарегистрироваться на шлюзе. Попробовать зарегистрироваться? - + Error: Bitmessage addresses start with BM- Please check the recipient address %1 Ошибка: адреса Bitmessage начинаются с "BM-". Пожалуйста, проверьте адрес получателя %1. - + Error: The recipient address %1 is not typed or copied correctly. Please check it. Ошибка: адрес получателя %1 набран или скопирован неправильно. Пожалуйста, проверьте его. - + Error: The recipient address %1 contains invalid characters. Please check it. Ошибка: адрес получателя %1 содержит недопустимые символы. Пожалуйста, проверьте его. - + Error: The version of the recipient address %1 is too high. Either you need to upgrade your Bitmessage software or your acquaintance is being clever. Ошибка: версия адреса получателя %1 слишком высокая. Либо вам нужно обновить программу Bitmessage, либо ваш знакомый - умник. - + Error: Some data encoded in the recipient address %1 is too short. There might be something wrong with the software of your acquaintance. Ошибка: часть данных, закодированных в адресе получателя %1 слишком короткая. Видимо, что-то не так с программой, используемой вашим знакомым. - + Error: Some data encoded in the recipient address %1 is too long. There might be something wrong with the software of your acquaintance. Ошибка: часть данных, закодированных в адресе получателя %1 слишком длинная. Видимо, что-то не так с программой, используемой вашим знакомым. - + Error: Some data encoded in the recipient address %1 is malformed. There might be something wrong with the software of your acquaintance. Ошибка: часть данных, закодированных в адресе получателя %1 сформирована неправильно. Видимо, что-то не так с программой, используемой вашим знакомым. - + Error: Something is wrong with the recipient address %1. Ошибка: что-то не так с адресом получателя %1. - + + Error: %1 + Ошибка: %1 + + + From %1 От %1 - + Synchronisation pending Ожидается синхронизация - + Bitmessage hasn't synchronised with the network, %n object(s) to be downloaded. If you quit now, it may cause delivery delays. Wait until the synchronisation finishes? Bitmessage не синхронизирован с сетью, незагруженных объектов: %n. Выход сейчас может привести к задержкам доставки. Подождать завершения синхронизации?Bitmessage не синхронизирован с сетью, незагруженных объектов: %n. Выход сейчас может привести к задержкам доставки. Подождать завершения синхронизации?Bitmessage не синхронизирован с сетью, незагруженных объектов: %n. Выход сейчас может привести к задержкам доставки. Подождать завершения синхронизации?Bitmessage не синхронизирован с сетью, незагруженных объектов: %n. Выход сейчас может привести к задержкам доставки. Подождать завершения синхронизации? - + Not connected Не подключено - + Bitmessage isn't connected to the network. If you quit now, it may cause delivery delays. Wait until connected and the synchronisation finishes? Bitmessage не подключен к сети. Выход сейчас может привести к задержкам доставки. Подождать подключения и завершения синхронизации? - + Waiting for network connection... Ожидание сетевого подключения... - + Waiting for finishing synchronisation... Ожидание окончания синхронизации... - + You have already set a notification sound for this address book entry. Do you really want to overwrite it? У вас уже есть звук уведомления для этого адресата. Вы уверены, что хотите перезаписать звук уведомления? @@ -1868,37 +1878,37 @@ The 'Random Number' option is selected by default but deterministic ad Всего соединений: - + Since startup: С начала работы: - + Processed 0 person-to-person messages. Обработано 0 сообщений. - + Processed 0 public keys. Обработано 0 открытых ключей. - + Processed 0 broadcasts. Обработано 0 рассылок. - + Inventory lookups per second: 0 Поисков в каталоге в секунду: 0 - + Objects to be synced: Несинхронизированные объекты: - + Stream # № потока @@ -1908,37 +1918,37 @@ The 'Random Number' option is selected by default but deterministic ad - + Since startup on %1 - С начала работы в %1 + С начала работы, %1 - + Down: %1/s Total: %2 Загрузка: %1/s Всего: %2 - + Up: %1/s Total: %2 Отправка: %1/s Всего: %2 - + Total Connections: %1 Всего соединений: %1 - + Inventory lookups per second: %1 Поисков в каталоге в секунду: %1 - + Up: 0 kB/s Отправка: 0 кБ/с - + Down: 0 kB/s Загрузка: 0 кБ/с @@ -1948,72 +1958,72 @@ The 'Random Number' option is selected by default but deterministic ad Состояние сети - + byte(s) байтбайтбайтбайт - + Object(s) to be synced: %n Несинхронизированные объекты: %nНесинхронизированные объекты: %nНесинхронизированные объекты: %nНесинхронизированные объекты: %n - + Processed %n person-to-person message(s). Обработано %n сообщение.Обработано %n сообщений.Обработано %n сообщений.Обработано %n сообщений. - + Processed %n broadcast message(s). Обработана %n рассылка.Обработано %n рассылок.Обработано %n рассылок.Обработано %n рассылок. - + Processed %n public key(s). Обработан %n открытый ключ.Обработано %n открытых ключей.Обработано %n открытых ключей.Обработано %n открытых ключей. - + Peer Узел - + IP address or hostname Адрес IP или имя узла - + Rating Рейтинг - + PyBitmessage tracks the success rate of connection attempts to individual nodes. The rating ranges from -1 to 1 and affects the likelihood of selecting the node in the future PyBitmessage отслеживает шанс успеха попыток подключения к отдельным узлам. Рейтинг варьируется в диапазоне от -1 до 1 и влияет на вероятность выбора узла в будущем. - + User agent Название приложения - + Peer's self-reported software Название ПО, как сообщает узел - + TLS TLS - + Connection encryption Шифрование соединения - + List of streams negotiated between you and the peer Перечень потоков, согласованных с конкретным узлом @@ -2072,7 +2082,7 @@ The 'Random Number' option is selected by default but deterministic ad - Chan passhphrase/name: + Chan passphrase/name: Пароль/имя чана: @@ -2094,17 +2104,17 @@ The 'Random Number' option is selected by default but deterministic ad newchandialog - + Successfully created / joined chan %1 Успешно создан / подключен чан %1 - + Chan creation / joining failed Не удалось создать / подключить чан - + Chan creation / joining cancelled Создание / подключение чана отменено From 6ce3ce6b2226c829b7778e6231b2563f86e35de8 Mon Sep 17 00:00:00 2001 From: Peter Surda Date: Thu, 30 Nov 2017 19:39:31 +0100 Subject: [PATCH 287/407] Chunking helper for sql statements with too many variables - preparation for #1081 --- src/helper_sql.py | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/src/helper_sql.py b/src/helper_sql.py index d27401cf..086cebfe 100644 --- a/src/helper_sql.py +++ b/src/helper_sql.py @@ -21,6 +21,24 @@ def sqlQuery(sqlStatement, *args): return queryreturn +def sqlExecuteChunked(sqlStatement, idCount, *args): + #SQLITE_MAX_VARIABLE_NUMBER, unfortunately getting/setting isn't exposed to python + sqlExecuteChunked.chunkSize = 999 + + if idCount == 0 or idCount > len(args): + return 0 + + totalRowCount = 0 + with sqlLock: + for i in range(len(args)-idCount, len(args), sqlExecuteChunked.chunkSize - (len(args)-idCount)): + sqlSubmitQueue.put(sqlStatement) + # first static args, and then iterative chunk + sqlSubmitQueue.put(args[0:len(args)-idCount] + args[i:i+sqlExecuteChunked.chunkSize - len(args)-idCount]) + retVal = sqlReturnQueue.get() + totalRowCount += retVal[1] + sqlSubmitQueue.put('commit') + return totalRowCount + def sqlExecute(sqlStatement, *args): sqlLock.acquire() sqlSubmitQueue.put(sqlStatement) From 48c0a2ae2ebd3ec046dfa3f4325a36588934985a Mon Sep 17 00:00:00 2001 From: Peter Surda Date: Thu, 30 Nov 2017 19:42:49 +0100 Subject: [PATCH 288/407] Arithmetic fix for preceding commit --- src/helper_sql.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/helper_sql.py b/src/helper_sql.py index 086cebfe..7410202b 100644 --- a/src/helper_sql.py +++ b/src/helper_sql.py @@ -33,7 +33,7 @@ def sqlExecuteChunked(sqlStatement, idCount, *args): for i in range(len(args)-idCount, len(args), sqlExecuteChunked.chunkSize - (len(args)-idCount)): sqlSubmitQueue.put(sqlStatement) # first static args, and then iterative chunk - sqlSubmitQueue.put(args[0:len(args)-idCount] + args[i:i+sqlExecuteChunked.chunkSize - len(args)-idCount]) + sqlSubmitQueue.put(args[0:len(args)-idCount] + args[i:i+sqlExecuteChunked.chunkSize - (len(args)-idCount)]) retVal = sqlReturnQueue.get() totalRowCount += retVal[1] sqlSubmitQueue.put('commit') From 6c224447a62931a468b45f7438d67dc2e4fcbfa8 Mon Sep 17 00:00:00 2001 From: Peter Surda Date: Thu, 30 Nov 2017 19:44:03 +0100 Subject: [PATCH 289/407] Minor fixes that came over BM - typo in quit confirmation dialog - nicer traceback in unhandled exceptions --- src/bitmessageqt/__init__.py | 2 +- src/debug.py | 4 +--- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/src/bitmessageqt/__init__.py b/src/bitmessageqt/__init__.py index 4cda426d..8ae31e8a 100644 --- a/src/bitmessageqt/__init__.py +++ b/src/bitmessageqt/__init__.py @@ -2618,7 +2618,7 @@ class MyForm(settingsmixin.SMainWindow): QtGui.QMessageBox.Yes|QtGui.QMessageBox.No|QtGui.QMessageBox.Cancel, QtGui.QMessageBox.Cancel) if reply == QtGui.QMessageBox.No: waitForPow = False - elif reply == QtGui.QMessage.Cancel: + elif reply == QtGui.QMessageBox.Cancel: return if PendingDownloadQueue.totalSize() > 0: diff --git a/src/debug.py b/src/debug.py index 83b11835..b03e8bf9 100644 --- a/src/debug.py +++ b/src/debug.py @@ -20,7 +20,6 @@ import logging import logging.config import os import sys -import traceback import helper_startup import state helper_startup.loadConfig() @@ -30,8 +29,7 @@ helper_startup.loadConfig() log_level = 'WARNING' def log_uncaught_exceptions(ex_cls, ex, tb): - logging.critical(''.join(traceback.format_tb(tb))) - logging.critical('{0}: {1}'.format(ex_cls, ex)) + logging.critical('Unhandled exception', exc_info=(ex_cls, ex, tb)) def configureLogging(): have_logging = False From 4ee9d0544692332a596e183d9ad9e4e0672c90a9 Mon Sep 17 00:00:00 2001 From: Peter Surda Date: Thu, 30 Nov 2017 20:08:14 +0100 Subject: [PATCH 290/407] Randomise key order during decryption - may help against timing/radio attacks --- src/class_objectProcessor.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/class_objectProcessor.py b/src/class_objectProcessor.py index ec2b32b9..21de6856 100644 --- a/src/class_objectProcessor.py +++ b/src/class_objectProcessor.py @@ -361,7 +361,7 @@ class objectProcessor(threading.Thread): # This is not an acknowledgement bound for me. See if it is a message # bound for me by trying to decrypt it with my private keys. - for key, cryptorObject in shared.myECCryptorObjects.items(): + for key, cryptorObject in sorted(shared.myECCryptorObjects.items(), key=lambda x: random.random()): try: if initialDecryptionSuccessful: # continue decryption attempts to avoid timing attacks cryptorObject.decrypt(data[readPosition:]) @@ -634,7 +634,7 @@ class objectProcessor(threading.Thread): """ signedData = data[8:readPosition] initialDecryptionSuccessful = False - for key, cryptorObject in shared.MyECSubscriptionCryptorObjects.items(): + for key, cryptorObject in sorted(shared.MyECSubscriptionCryptorObjects.items(), key=lambda x: random.random()): try: if initialDecryptionSuccessful: # continue decryption attempts to avoid timing attacks cryptorObject.decrypt(data[readPosition:]) From 3b86dfc639ba6fd77d69aed0b1a71b2259c08c5e Mon Sep 17 00:00:00 2001 From: Peter Surda Date: Sat, 2 Dec 2017 00:45:01 +0100 Subject: [PATCH 291/407] Log missing indicator plugin --- src/bitmessageqt/__init__.py | 1 + 1 file changed, 1 insertion(+) diff --git a/src/bitmessageqt/__init__.py b/src/bitmessageqt/__init__.py index 8ae31e8a..c731b469 100644 --- a/src/bitmessageqt/__init__.py +++ b/src/bitmessageqt/__init__.py @@ -1294,6 +1294,7 @@ class MyForm(settingsmixin.SMainWindow): try: self.indicatorUpdate = get_plugin('indicator')(self) except (NameError, TypeError): + logger.warning("No indicator plugin found") self.indicatorUpdate = _noop_update # initialise the message notifier From 41ead2bfb5d0d2e61c9559042814b45b246d4c91 Mon Sep 17 00:00:00 2001 From: Peter Surda Date: Sat, 2 Dec 2017 00:48:08 +0100 Subject: [PATCH 292/407] Distribute downloads more evenly - also increases expireation of missing objects from 10 minutes to an hour --- src/network/downloadthread.py | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/network/downloadthread.py b/src/network/downloadthread.py index 8c384edc..0cbdac16 100644 --- a/src/network/downloadthread.py +++ b/src/network/downloadthread.py @@ -13,10 +13,10 @@ from state import missingObjects class DownloadThread(threading.Thread, StoppableThread): minPending = 200 - requestChunk = 1000 + maxRequestChunk = 1000 requestTimeout = 60 cleanInterval = 60 - requestExpires = 600 + requestExpires = 3600 def __init__(self): threading.Thread.__init__(self, name="Downloader") @@ -42,6 +42,7 @@ class DownloadThread(threading.Thread, StoppableThread): # Choose downloading peers randomly connections = BMConnectionPool().inboundConnections.values() + BMConnectionPool().outboundConnections.values() random.shuffle(connections) + requestChunk = max(int(DownloadThread.maxRequestChunk / len(connections)), 1) for i in connections: now = time.time() timedOut = now - DownloadThread.requestTimeout @@ -61,8 +62,8 @@ class DownloadThread(threading.Thread, StoppableThread): random.shuffle(request) if not request: continue - if len(request) > DownloadThread.requestChunk - downloadPending: - request = request[:DownloadThread.requestChunk - downloadPending] + if len(request) > requestChunk - downloadPending: + request = request[:requestChunk - downloadPending] # mark them as pending for k in request: i.objectsNewToMe[k] = False From a3398d6a17e623da780744148be33d4ea7fda02b Mon Sep 17 00:00:00 2001 From: Peter Surda Date: Sat, 2 Dec 2017 00:50:58 +0100 Subject: [PATCH 293/407] Don't crash download thread if no connections --- src/network/downloadthread.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/network/downloadthread.py b/src/network/downloadthread.py index 0cbdac16..5fe1ee25 100644 --- a/src/network/downloadthread.py +++ b/src/network/downloadthread.py @@ -42,7 +42,10 @@ class DownloadThread(threading.Thread, StoppableThread): # Choose downloading peers randomly connections = BMConnectionPool().inboundConnections.values() + BMConnectionPool().outboundConnections.values() random.shuffle(connections) - requestChunk = max(int(DownloadThread.maxRequestChunk / len(connections)), 1) + try: + requestChunk = max(int(DownloadThread.maxRequestChunk / len(connections)), 1) + except ZeroDivisionError: + requestChunk = 1 for i in connections: now = time.time() timedOut = now - DownloadThread.requestTimeout From 5605672f753162a82eb7651f3dc3d20d0502ec20 Mon Sep 17 00:00:00 2001 From: Peter Surda Date: Sat, 2 Dec 2017 02:48:10 +0100 Subject: [PATCH 294/407] Fix tag search when inventory contains blobs - recent changes caused the "tag" (and "payload") columns in the inventory table in messages.dat to be stored as blobs. Searches by tag (e.g. pubkey lookups) stopped working. This fixes it. --- src/storage/sqlite.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/storage/sqlite.py b/src/storage/sqlite.py index aba64244..7c9e8822 100644 --- a/src/storage/sqlite.py +++ b/src/storage/sqlite.py @@ -1,6 +1,7 @@ import collections from threading import current_thread, enumerate as threadingEnumerate, RLock import Queue +import sqlite3 import time from helper_sql import * @@ -50,7 +51,7 @@ class SqliteInventory(InventoryStorage): def by_type_and_tag(self, objectType, tag): with self.lock: values = [value for value in self._inventory.values() if value.type == objectType and value.tag == tag] - values += (InventoryItem(*value) for value in sqlQuery('SELECT objecttype, streamnumber, payload, expirestime, tag FROM inventory WHERE objecttype=? AND tag=?', objectType, tag)) + values += (InventoryItem(*value) for value in sqlQuery('SELECT objecttype, streamnumber, payload, expirestime, tag FROM inventory WHERE objecttype=? AND tag=?', objectType, sqlite3.Binary(tag))) return values def hashes_by_stream(self, stream): From dd780f8d806afae29db5c7173d3e3a9eb1bcfb2e Mon Sep 17 00:00:00 2001 From: sandakersmann Date: Thu, 7 Dec 2017 00:27:59 +0100 Subject: [PATCH 295/407] This type of data is called metadata --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 7a161d04..0ea6144b 100644 --- a/README.md +++ b/README.md @@ -5,7 +5,7 @@ Bitmessage is a P2P communications protocol used to send encrypted messages to another person or to many subscribers. It is decentralized and trustless, meaning that you need-not inherently trust any entities like root certificate authorities. It uses strong authentication, which means that the sender of a -message cannot be spoofed, and it aims to hide "non-content" data, like the +message cannot be spoofed, and it aims to hide metadata, like the sender and receiver of messages, from passive eavesdroppers like those running warrantless wiretapping programs. From 395812c0f8b50bcc56b411d4c293b7735382c32f Mon Sep 17 00:00:00 2001 From: Peter Surda Date: Wed, 20 Dec 2017 09:20:24 +0100 Subject: [PATCH 296/407] Systemd config file - tested on Debian 9, you may have to adjust paths/uids if your deployment differs --- packages/systemd/bitmessage.service | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) create mode 100644 packages/systemd/bitmessage.service diff --git a/packages/systemd/bitmessage.service b/packages/systemd/bitmessage.service new file mode 100644 index 00000000..1a9f7f47 --- /dev/null +++ b/packages/systemd/bitmessage.service @@ -0,0 +1,18 @@ +[Unit] +Description=Bitmessage Daemon +After=network.target auditd.service + +[Service] +ExecStart=/usr/bin/python2 /usr/src/PyBitmessage/src/bitmessagemain.py +ExecReload=/bin/kill -HUP $MAINPID +KillMode=process +Restart=on-failure +Type=forking +PIDFile=/var/lib/bitmessage/.config/PyBitmessage/singleton.lock +User=bitmessage +Group=nogroup +WorkingDirectory=/var/lib/bitmessage +Environment="HOME=/var/lib/bitmessage" + +[Install] +WantedBy=multi-user.target From 6fb5a751c624efce8bc41736d219b0acda7cebaa Mon Sep 17 00:00:00 2001 From: Peter Surda Date: Wed, 20 Dec 2017 09:41:36 +0100 Subject: [PATCH 297/407] Add collectd monitoring script --- packages/collectd/pybitmessagestatus.py | 60 +++++++++++++++++++++++++ 1 file changed, 60 insertions(+) create mode 100644 packages/collectd/pybitmessagestatus.py diff --git a/packages/collectd/pybitmessagestatus.py b/packages/collectd/pybitmessagestatus.py new file mode 100644 index 00000000..1db9f5b1 --- /dev/null +++ b/packages/collectd/pybitmessagestatus.py @@ -0,0 +1,60 @@ +#!/usr/bin/env python2.7 + +import collectd +import json +import xmlrpclib + +pybmurl = "" +api = "" + +def init_callback(): + global api + api = xmlrpclib.ServerProxy(pybmurl) + collectd.info('pybitmessagestatus.py init done') + +def config_callback(ObjConfiguration): + global pybmurl + apiUsername = "" + apiPassword = "" + apiInterface = "127.0.0.1" + apiPort = 8445 + for node in ObjConfiguration.children: + key = node.key.lower() + if key.lower() == "apiusername" and node.values: + apiUsername = node.values[0] + elif key.lower() == "apipassword" and node.values: + apiPassword = node.values[0] + elif key.lower() == "apiinterface" and node.values: + apiInterface = node.values[0] + elif key.lower() == "apiport" and node.values: + apiPort = node.values[0] + pybmurl = "http://" + apiUsername + ":" + apiPassword + "@" + apiInterface+ ":" + str(int(apiPort)) + "/" + collectd.info('pybitmessagestatus.py config done') + +def read_callback(): + try: + clientStatus = json.loads(api.clientStatus()) + except: + collectd.info("Exception loading or parsing JSON") + return + + for i in ["networkConnections", "numberOfPubkeysProcessed", "numberOfMessagesProcessed", "numberOfBroadcastsProcessed"]: + metric = collectd.Values() + metric.plugin = "pybitmessagestatus" + if i[0:6] == "number": + metric.type = 'counter' + else: + metric.type = 'gauge' + metric.type_instance = i.lower() + try: + metric.values = [clientStatus[i]] + except: + collectd.info("Value for %s missing" % (i)) + metric.dispatch() + +if __name__ == "__main__": + main() +else: + collectd.register_init(init_callback) + collectd.register_config(config_callback) + collectd.register_read(read_callback) From 3cb9547389e41bbf8b7cd8e258e91db57614e4b6 Mon Sep 17 00:00:00 2001 From: Peter Surda Date: Thu, 21 Dec 2017 14:26:51 +0100 Subject: [PATCH 298/407] Only write PID after last fork - should fix systemd integration --- src/bitmessagemain.py | 2 +- src/singleinstance.py | 9 +++++---- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/src/bitmessagemain.py b/src/bitmessagemain.py index 83a41919..91032fe5 100755 --- a/src/bitmessagemain.py +++ b/src/bitmessagemain.py @@ -363,7 +363,7 @@ class Main: # fork not implemented pass else: - shared.thisapp.lock() # relock + shared.thisapp.lock(True) # relock and write pid shared.thisapp.lockPid = None # indicate we're the final child sys.stdout.flush() sys.stderr.flush() diff --git a/src/singleinstance.py b/src/singleinstance.py index 7a025945..883c83cc 100644 --- a/src/singleinstance.py +++ b/src/singleinstance.py @@ -36,7 +36,7 @@ class singleinstance: self.initialized = True atexit.register(self.cleanup) - def lock(self): + def lock(self, writePid = False): if self.lockPid is None: self.lockPid = os.getpid() if sys.platform == 'win32': @@ -68,9 +68,10 @@ class singleinstance: sys.exit(-1) else: pidLine = "%i\n" % self.lockPid - self.fp.truncate(0) - self.fp.write(pidLine) - self.fp.flush() + if writePid: + self.fp.truncate(0) + self.fp.write(pidLine) + self.fp.flush() def cleanup(self): if not self.initialized: From a0e5ee4ccedf9c744b345be38c1db08bff1e052d Mon Sep 17 00:00:00 2001 From: sigoa Date: Tue, 26 Dec 2017 14:17:37 +0100 Subject: [PATCH 299/407] utf chan name support, various fixes no unused variables etc. any longer --- src/bitmessagecli.py | 45 ++++++++++++++++++++++---------------------- 1 file changed, 23 insertions(+), 22 deletions(-) diff --git a/src/bitmessagecli.py b/src/bitmessagecli.py index ee877818..e216a91a 100644 --- a/src/bitmessagecli.py +++ b/src/bitmessagecli.py @@ -1,14 +1,15 @@ -#!/usr/bin/python2.7 +#!/usr/bin/python2.7 +# -*- coding: utf-8 -*- # Created by Adam Melton (.dok) referenceing https://bitmessage.org/wiki/API_Reference for API documentation # Distributed under the MIT/X11 software license. See http://www.opensource.org/licenses/mit-license.php. -# This is an example of a daemon client for PyBitmessage 0.4.2, by .dok (Version 0.3.0) +# This is an example of a daemon client for PyBitmessage 0.4.3, by .dok (Version 0.3.1) import xmlrpclib import datetime -import hashlib -import getopt +#import hashlib +#import getopt import imghdr import ntpath import json @@ -37,7 +38,7 @@ def userInput(message): #Checks input for exit or quit. Also formats for input, elif (uInput.lower() == 'quit'): #Quits the program print '\n Bye\n' sys.exit() - os.exit() + os._exit() # _ else: return uInput @@ -55,7 +56,7 @@ def lookupAppdataFolder(): #gets the appropriate folders for the .dat files depe dataFolder = path.join(os.environ["HOME"], "Library/Application support/", APPNAME) + '/' else: print ' Could not find home folder, please report this message and your OS X version to the Daemon Github.' - os.exit() + os._exit() elif 'win32' in sys.platform or 'win64' in sys.platform: dataFolder = path.join(environ['APPDATA'], APPNAME) + '\\' @@ -117,7 +118,7 @@ def apiInit(apiEnabled): apiUsr = userInput("API Username") apiPwd = userInput("API Password") - apiInterface = userInput("API Interface. (127.0.0.1)") + #apiInterface = userInput("API Interface. (127.0.0.1)") apiPort = userInput("API Port") apiEnabled = userInput("API Enabled? (True) or (False)").lower() daemon = userInput("Daemon mode Enabled? (True) or (False)").lower() @@ -207,7 +208,7 @@ def apiData(): apiInit("") #Initalize the keys.dat file with API information #keys.dat file was found or appropriately configured, allow information retrieval - apiEnabled = apiInit(BMConfigParser().safeGetBoolean('bitmessagesettings','apienabled')) #if false it will prompt the user, if true it will return true + #apiEnabled = apiInit(BMConfigParser().safeGetBoolean('bitmessagesettings','apienabled')) #if false it will prompt the user, if true it will return true BMConfigParser().read(keysPath)#read again since changes have been made apiPort = int(BMConfigParser().get('bitmessagesettings', 'apiport')) @@ -424,7 +425,7 @@ def unsubscribe(): break - uInput = userInput("Are you sure, (Y)es or (N)o?").lower() + userInput("Are you sure, (Y)es or (N)o?").lower() # #uInput = api.deleteSubscription(address) print ('\n You are now unsubscribed from: ' + address + '\n') @@ -522,9 +523,9 @@ def listAdd(): #Lists all of the addresses and their info print ' | # | Label | Address |S#|Enabled|' print ' |---|-------------------|-------------------------------------|--|-------|' for addNum in range (0, numAddresses): #processes all of the addresses and lists them out - label = str(jsonAddresses['addresses'][addNum]['label']) + label = (jsonAddresses['addresses'][addNum]['label' ]).encode('utf') # may still misdiplay in some consoles address = str(jsonAddresses['addresses'][addNum]['address']) - stream = str(jsonAddresses['addresses'][addNum]['stream']) + stream = str(jsonAddresses['addresses'][addNum]['stream']) enabled = str(jsonAddresses['addresses'][addNum]['enabled']) if (len(label) > 19): @@ -593,7 +594,7 @@ def genMilAddr(): #Generate address lbl = "random" + str(i) addressLabel = lbl.encode('base64') try: - generatedAddress = api.createRandomAddress(addressLabel) + api.createRandomAddress(addressLabel) # generatedAddress = except: print '\n Connection Error\n' usrPrompt = 0 @@ -912,7 +913,7 @@ def inbox(unreadOnly = False): #Lists the messages by: Message Number, To Addres if not message['read']: messagesUnread += 1 if (messagesPrinted%20 == 0 and messagesPrinted != 0): - uInput = userInput('(Press Enter to continue or type (Exit) to return to the main menu.)').lower() + userInput('(Press Enter to continue or type (Exit) to return to the main menu.)').lower() # uInput = print '\n -----------------------------------' print ' There are %d unread messages of %d messages in the inbox.' % (messagesUnread, numMessages) @@ -940,7 +941,7 @@ def outbox(): print ' Last Action Time:', datetime.datetime.fromtimestamp(float(outboxMessages['sentMessages'][msgNum]['lastActionTime'])).strftime('%Y-%m-%d %H:%M:%S') if (msgNum%20 == 0 and msgNum != 0): - uInput = userInput('(Press Enter to continue or type (Exit) to return to the main menu.)').lower() + userInput('(Press Enter to continue or type (Exit) to return to the main menu.)').lower() # uInput = print '\n -----------------------------------' print ' There are ',numMessages,' messages in the outbox.' @@ -1319,12 +1320,12 @@ def UI(usrInput): #Main user menu print ' |------------------------|----------------------------------------------|' print ' | subscribe | Subscribes to an address |' print ' | unsubscribe | Unsubscribes from an address |' - #print ' | listSubscriptions | Lists all of the subscriptions. |' + #print ' | listSubscriptions | Lists all of the subscriptions. |' print ' |------------------------|----------------------------------------------|' - print ' | create | Creates a channel |' + print ' | create | Creates a channel |' print ' | join | Joins a channel |' print ' | leave | Leaves a channel |' - print ' |------------------------|----------------------------------------------|' + print ' |------------------------|----------------------------------------------|' print ' | inbox | Lists the message information for the inbox |' print ' | outbox | Lists the message information for the outbox |' print ' | send | Send a new message or broadcast |' @@ -1368,7 +1369,7 @@ def UI(usrInput): #Main user menu elif usrInput == "quit": #Quits the application print '\n Bye\n' sys.exit() - os.exit() + os._exit() elif usrInput == "listaddresses": #Lists all of the identities in the addressbook listAdd() @@ -1445,17 +1446,17 @@ def UI(usrInput): #Main user menu elif usrInput == "create": createChan() - userPrompt = 1 + usrPrompt = 1 main() elif usrInput == "join": joinChan() - userPrompt = 1 + usrPrompt = 1 main() elif usrInput == "leave": leaveChan() - userPrompt = 1 + usrPrompt = 1 main() elif usrInput == "inbox": @@ -1668,7 +1669,7 @@ def UI(usrInput): #Main user menu usrPrompt = 1 else: print '\n Invalid Entry.\n' - userPrompt = 1 + usrPrompt = 1 main() elif usrInput == "exit": From 394d564bdca86b88d60d058a7f1966a1d884fd76 Mon Sep 17 00:00:00 2001 From: sigoa Date: Tue, 26 Dec 2017 15:01:12 +0100 Subject: [PATCH 300/407] minor mod of ver. not a biggie --- src/bitmessagecli.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/bitmessagecli.py b/src/bitmessagecli.py index e216a91a..d7f4e5a9 100644 --- a/src/bitmessagecli.py +++ b/src/bitmessagecli.py @@ -3,7 +3,7 @@ # Created by Adam Melton (.dok) referenceing https://bitmessage.org/wiki/API_Reference for API documentation # Distributed under the MIT/X11 software license. See http://www.opensource.org/licenses/mit-license.php. -# This is an example of a daemon client for PyBitmessage 0.4.3, by .dok (Version 0.3.1) +# This is an example of a daemon client for PyBitmessage 0.6.2, by .dok (Version 0.3.1) , modified import xmlrpclib @@ -1320,7 +1320,7 @@ def UI(usrInput): #Main user menu print ' |------------------------|----------------------------------------------|' print ' | subscribe | Subscribes to an address |' print ' | unsubscribe | Unsubscribes from an address |' - #print ' | listSubscriptions | Lists all of the subscriptions. |' + #print' | listSubscriptions | Lists all of the subscriptions. |' print ' |------------------------|----------------------------------------------|' print ' | create | Creates a channel |' print ' | join | Joins a channel |' @@ -1741,7 +1741,7 @@ def main(): if (usrPrompt == 0): print '\n ------------------------------' print ' | Bitmessage Daemon by .dok |' - print ' | Version 0.2.6 for BM 0.3.5 |' + print ' | Version 0.3.1 for BM 0.6.2 |' print ' ------------------------------' api = xmlrpclib.ServerProxy(apiData()) #Connect to BitMessage using these api credentials @@ -1770,4 +1770,4 @@ def main(): UI("quit") if __name__ == "__main__": - main() + main() From 02490e3286ad1d865bdc9ec4552766a0304f6b37 Mon Sep 17 00:00:00 2001 From: Peter Surda Date: Fri, 29 Dec 2017 08:41:15 +0100 Subject: [PATCH 301/407] Don't break if over 50k messages - typo if there were over 50k messages in inventory caused PyBM to stall --- src/network/tcp.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/network/tcp.py b/src/network/tcp.py index 70e22e08..922aa79d 100644 --- a/src/network/tcp.py +++ b/src/network/tcp.py @@ -180,7 +180,7 @@ class TCPConnection(BMProto, TLSDispatcher): payload += hash objectCount += 1 if objectCount >= BMProto.maxObjectCount: - self.sendChunk() + sendChunk() payload = b'' objectCount = 0 From e9b1aa48a91624db922d832a1d342e63ad896b4e Mon Sep 17 00:00:00 2001 From: Peter Surda Date: Fri, 29 Dec 2017 08:49:08 +0100 Subject: [PATCH 302/407] Protocol error handler fixes - was broken if there was no error message in "raise" - added default texts for network exceptions --- src/network/bmobject.py | 17 +++++++++++------ src/network/bmproto.py | 9 ++++++--- src/network/proxy.py | 2 +- 3 files changed, 18 insertions(+), 10 deletions(-) diff --git a/src/network/bmobject.py b/src/network/bmobject.py index 249ec2ab..267cac58 100644 --- a/src/network/bmobject.py +++ b/src/network/bmobject.py @@ -8,23 +8,28 @@ from network.dandelion import Dandelion import protocol import state -class BMObjectInsufficientPOWError(Exception): pass +class BMObjectInsufficientPOWError(Exception): + errorCodes = ("Insufficient proof of work") -class BMObjectInvalidDataError(Exception): pass +class BMObjectInvalidDataError(Exception): + errorCodes = ("Data invalid") -class BMObjectExpiredError(Exception): pass +class BMObjectExpiredError(Exception): + errorCodes = ("Object expired") -class BMObjectUnwantedStreamError(Exception): pass +class BMObjectUnwantedStreamError(Exception): + errorCodes = ("Object in unwanted stream") -class BMObjectInvalidError(Exception): pass +class BMObjectInvalidError(Exception): + errorCodes = ("Invalid object") class BMObjectAlreadyHaveError(Exception): - pass + errorCodes = ("Already have this object") class BMObject(object): diff --git a/src/network/bmproto.py b/src/network/bmproto.py index 17b2c761..47c6c858 100644 --- a/src/network/bmproto.py +++ b/src/network/bmproto.py @@ -24,13 +24,16 @@ import shared import state import protocol -class BMProtoError(ProxyError): pass +class BMProtoError(ProxyError): + errorCodes = ("Protocol error") -class BMProtoInsufficientDataError(BMProtoError): pass +class BMProtoInsufficientDataError(BMProtoError): + errorCodes = ("Insufficient data") -class BMProtoExcessiveDataError(BMProtoError): pass +class BMProtoExcessiveDataError(BMProtoError): + errorCodes = ("Too much data") class BMProto(AdvancedDispatcher, ObjectTracker): diff --git a/src/network/proxy.py b/src/network/proxy.py index 7d46cd86..96930c18 100644 --- a/src/network/proxy.py +++ b/src/network/proxy.py @@ -10,7 +10,7 @@ import state class ProxyError(Exception): errorCodes = ("UnknownError") - def __init__(self, code): + def __init__(self, code=-1): self.code = code try: self.message = self.__class__.errorCodes[self.code] From bcc5a210a484dd76b7e9ad3b033f47e704026770 Mon Sep 17 00:00:00 2001 From: Peter Surda Date: Fri, 29 Dec 2017 09:13:41 +0100 Subject: [PATCH 303/407] Fix PID file if not daemonized --- src/singleinstance.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/singleinstance.py b/src/singleinstance.py index 883c83cc..fdb5ee98 100644 --- a/src/singleinstance.py +++ b/src/singleinstance.py @@ -31,7 +31,7 @@ class singleinstance: import bitmessageqt bitmessageqt.init() - self.lock() + self.lock(not daemon) self.initialized = True atexit.register(self.cleanup) From 1864762a0a3af4f32ba7fc5994bcb51d76643da3 Mon Sep 17 00:00:00 2001 From: Peter Surda Date: Mon, 1 Jan 2018 12:49:08 +0100 Subject: [PATCH 304/407] Apply bandwidth limits without restart - also minor style fixes --- src/bitmessageqt/__init__.py | 10 +++++----- src/network/asyncore_pollchoose.py | 4 ++-- src/network/connectionpool.py | 4 ++-- 3 files changed, 9 insertions(+), 9 deletions(-) diff --git a/src/bitmessageqt/__init__.py b/src/bitmessageqt/__init__.py index c731b469..e6ff0cd0 100644 --- a/src/bitmessageqt/__init__.py +++ b/src/bitmessageqt/__init__.py @@ -69,7 +69,7 @@ import queues import shutdown import state from statusbar import BMStatusBar -import throttle +from network.asyncore_pollchoose import set_rates from version import softwareVersion import sound @@ -2288,16 +2288,16 @@ class MyForm(settingsmixin.SMainWindow): int(float(self.settingsDialogInstance.ui.lineEditMaxDownloadRate.text())))) BMConfigParser().set('bitmessagesettings', 'maxuploadrate', str( int(float(self.settingsDialogInstance.ui.lineEditMaxUploadRate.text())))) - except: + except ValueError: QMessageBox.about(self, _translate("MainWindow", "Number needed"), _translate( "MainWindow", "Your maximum download and upload rate must be numbers. Ignoring what you typed.")) + else: + set_rates(BMConfigParser().safeGetInt("bitmessagesettings", "maxdownloadrate"), + BMConfigParser().safeGetInt("bitmessagesettings", "maxuploadrate")) BMConfigParser().set('bitmessagesettings', 'maxoutboundconnections', str( int(float(self.settingsDialogInstance.ui.lineEditMaxOutboundConnections.text())))) - throttle.SendThrottle().resetLimit() - throttle.ReceiveThrottle().resetLimit() - BMConfigParser().set('bitmessagesettings', 'namecoinrpctype', self.settingsDialogInstance.getNamecoinType()) BMConfigParser().set('bitmessagesettings', 'namecoinrpchost', str( diff --git a/src/network/asyncore_pollchoose.py b/src/network/asyncore_pollchoose.py index caa9d650..c5586a91 100644 --- a/src/network/asyncore_pollchoose.py +++ b/src/network/asyncore_pollchoose.py @@ -129,8 +129,8 @@ def write(obj): def set_rates(download, upload): global maxDownloadRate, maxUploadRate, downloadBucket, uploadBucket, downloadTimestamp, uploadTimestamp - maxDownloadRate = float(download) - maxUploadRate = float(upload) + maxDownloadRate = float(download) * 1024 + maxUploadRate = float(upload) * 1024 downloadBucket = maxDownloadRate uploadBucket = maxUploadRate downloadTimestamp = time.time() diff --git a/src/network/connectionpool.py b/src/network/connectionpool.py index 2f937a15..3b817e65 100644 --- a/src/network/connectionpool.py +++ b/src/network/connectionpool.py @@ -22,8 +22,8 @@ import state class BMConnectionPool(object): def __init__(self): asyncore.set_rates( - BMConfigParser().safeGetInt("bitmessagesettings", "maxdownloadrate") * 1024, - BMConfigParser().safeGetInt("bitmessagesettings", "maxuploadrate") * 1024) + BMConfigParser().safeGetInt("bitmessagesettings", "maxdownloadrate"), + BMConfigParser().safeGetInt("bitmessagesettings", "maxuploadrate")) self.outboundConnections = {} self.inboundConnections = {} self.listeningSockets = {} From 36cc5b9cf5e122b736854079b78e6b0dfb903bbc Mon Sep 17 00:00:00 2001 From: Peter Surda Date: Mon, 1 Jan 2018 12:51:35 +0100 Subject: [PATCH 305/407] Download optimisations - don't make empty download requests - use smaller chunks when they can be spread across multiple connections --- src/network/downloadthread.py | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/src/network/downloadthread.py b/src/network/downloadthread.py index 5fe1ee25..35616f1b 100644 --- a/src/network/downloadthread.py +++ b/src/network/downloadthread.py @@ -43,7 +43,7 @@ class DownloadThread(threading.Thread, StoppableThread): connections = BMConnectionPool().inboundConnections.values() + BMConnectionPool().outboundConnections.values() random.shuffle(connections) try: - requestChunk = max(int(DownloadThread.maxRequestChunk / len(connections)), 1) + requestChunk = max(int(min(DownloadThread.maxRequestChunk, len(missingObjects)) / len(connections)), 1) except ZeroDivisionError: requestChunk = 1 for i in connections: @@ -63,15 +63,14 @@ class DownloadThread(threading.Thread, StoppableThread): except KeyError: continue random.shuffle(request) + if len(request) > requestChunk - downloadPending: + request = request[:max(1, requestChunk - downloadPending)] if not request: continue - if len(request) > requestChunk - downloadPending: - request = request[:requestChunk - downloadPending] # mark them as pending for k in request: i.objectsNewToMe[k] = False missingObjects[k] = now - payload = bytearray() payload.extend(addresses.encodeVarint(len(request))) for chunk in request: From baba0ae206b6cc8f6d81725fb1184a5d70225ed9 Mon Sep 17 00:00:00 2001 From: Peter Surda Date: Mon, 1 Jan 2018 13:04:58 +0100 Subject: [PATCH 306/407] Remove obsolete code --- src/shutdown.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/shutdown.py b/src/shutdown.py index 278759e5..49c2fb9b 100644 --- a/src/shutdown.py +++ b/src/shutdown.py @@ -9,14 +9,12 @@ from helper_sql import sqlQuery, sqlStoredProcedure from helper_threading import StoppableThread from knownnodes import saveKnownNodes from inventory import Inventory -import protocol from queues import addressGeneratorQueue, objectProcessorQueue, UISignalQueue, workerQueue import shared import state def doCleanShutdown(): state.shutdown = 1 #Used to tell proof of work worker threads and the objectProcessorThread to exit. - protocol.broadcastToSendDataQueues((0, 'shutdown', 'no data')) objectProcessorQueue.put(('checkShutdownVariable', 'no data')) for thread in threading.enumerate(): if thread.isAlive() and isinstance(thread, StoppableThread): From d9a42630830eef597021179924351bba3b959386 Mon Sep 17 00:00:00 2001 From: Peter Surda Date: Mon, 1 Jan 2018 13:08:12 +0100 Subject: [PATCH 307/407] Daemonising fixes - change forking exit order as systemd expects (wait until child is ready, then exit parent, then grandparent) - fix signal handler if prctl not installed - revert recent PID file changes --- src/bitmessagemain.py | 19 ++++++++++++++++++- src/helper_generic.py | 2 +- src/singleinstance.py | 18 +++++++++++++----- 3 files changed, 32 insertions(+), 7 deletions(-) diff --git a/src/bitmessagemain.py b/src/bitmessagemain.py index 91032fe5..6f796939 100755 --- a/src/bitmessagemain.py +++ b/src/bitmessagemain.py @@ -342,13 +342,21 @@ class Main: sleep(1) def daemonize(self): + grandfatherPid = os.getpid() + parentPid = None try: if os.fork(): + # unlock + shared.thisapp.cleanup() + # wait until grandchild ready + while True: + sleep(1) os._exit(0) except AttributeError: # fork not implemented pass else: + parentPid = os.getpid() shared.thisapp.lock() # relock os.umask(0) try: @@ -358,12 +366,17 @@ class Main: pass try: if os.fork(): + # unlock + shared.thisapp.cleanup() + # wait until child ready + while True: + sleep(1) os._exit(0) except AttributeError: # fork not implemented pass else: - shared.thisapp.lock(True) # relock and write pid + shared.thisapp.lock() # relock shared.thisapp.lockPid = None # indicate we're the final child sys.stdout.flush() sys.stderr.flush() @@ -374,6 +387,10 @@ class Main: os.dup2(si.fileno(), sys.stdin.fileno()) os.dup2(so.fileno(), sys.stdout.fileno()) os.dup2(se.fileno(), sys.stderr.fileno()) + if parentPid: + # signal ready + os.kill(parentPid, signal.SIGTERM) + os.kill(grandfatherPid, signal.SIGTERM) def setSignalHandler(self): signal.signal(signal.SIGINT, helper_generic.signal_handler) diff --git a/src/helper_generic.py b/src/helper_generic.py index b750e519..4f7a1299 100644 --- a/src/helper_generic.py +++ b/src/helper_generic.py @@ -51,7 +51,7 @@ def signal_handler(signal, frame): raise SystemExit if "PoolWorker" in current_process().name: raise SystemExit - if current_thread().name != "PyBitmessage": + if current_thread().name not in ("PyBitmessage", "MainThread"): return logger.error("Got signal %i", signal) if BMConfigParser().safeGetBoolean('bitmessagesettings', 'daemon'): diff --git a/src/singleinstance.py b/src/singleinstance.py index fdb5ee98..d7cc0ab3 100644 --- a/src/singleinstance.py +++ b/src/singleinstance.py @@ -36,7 +36,7 @@ class singleinstance: self.initialized = True atexit.register(self.cleanup) - def lock(self, writePid = False): + def lock(self): if self.lockPid is None: self.lockPid = os.getpid() if sys.platform == 'win32': @@ -68,16 +68,24 @@ class singleinstance: sys.exit(-1) else: pidLine = "%i\n" % self.lockPid - if writePid: - self.fp.truncate(0) - self.fp.write(pidLine) - self.fp.flush() + self.fp.truncate(0) + self.fp.write(pidLine) + self.fp.flush() def cleanup(self): if not self.initialized: return if self.daemon and self.lockPid == os.getpid(): # these are the two initial forks while daemonizing + try: + if sys.platform == 'win32': + if hasattr(self, 'fd'): + os.close(self.fd) + else: + fcntl.lockf(self.fp, fcntl.LOCK_UN) + except Exception, e: + pass + return print "Cleaning up lockfile" try: From 6b54a4ab0e1ddfc39d06dca2d5aea010be7e8a5a Mon Sep 17 00:00:00 2001 From: Peter Surda Date: Mon, 1 Jan 2018 13:10:19 +0100 Subject: [PATCH 308/407] Daemonize fix - forgot to revert a line in previous commit --- src/singleinstance.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/singleinstance.py b/src/singleinstance.py index d7cc0ab3..ed1048ba 100644 --- a/src/singleinstance.py +++ b/src/singleinstance.py @@ -31,7 +31,7 @@ class singleinstance: import bitmessageqt bitmessageqt.init() - self.lock(not daemon) + self.lock() self.initialized = True atexit.register(self.cleanup) From bb5f1d6f98b3dabec7a432bcde399bbec12c0b8a Mon Sep 17 00:00:00 2001 From: Peter Surda Date: Tue, 2 Jan 2018 10:29:21 +0100 Subject: [PATCH 309/407] Setup.py typo - surprisingly, it only was broken on some systems, e.g. Debian 8 --- setup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.py b/setup.py index 0851b78f..ba34f6df 100644 --- a/setup.py +++ b/setup.py @@ -83,7 +83,7 @@ if __name__ == "__main__": 'qrcode': ['qrcode'], 'pyopencl': ['pyopencl'], 'notify2': ['notify2'], - 'sound:platform_system=="Windows"': ['winsound'] + 'sound;platform_system=="Windows"': ['winsound'] }, classifiers=[ "License :: OSI Approved :: MIT License" From 9b58f35b80e1ece4f6d8fdefcc8ec13fce687b08 Mon Sep 17 00:00:00 2001 From: Peter Surda Date: Tue, 2 Jan 2018 13:56:03 +0100 Subject: [PATCH 310/407] App name in version --- src/version.py | 1 + 1 file changed, 1 insertion(+) diff --git a/src/version.py b/src/version.py index bd06ca9b..694f71e4 100644 --- a/src/version.py +++ b/src/version.py @@ -1 +1,2 @@ +softwareName = 'PyBitmessage' softwareVersion = '0.6.2' From 8788f2d3497e061d14e9ae4bd0864c6ea2373b39 Mon Sep 17 00:00:00 2001 From: Peter Surda Date: Tue, 2 Jan 2018 14:29:21 +0100 Subject: [PATCH 311/407] Server full and duplicate handling - will try to report "Server full" over protocol for 10 extra connections over limit, instead of simply dropping them - if connected to the same host inbound and outbound, handle as server full (prevents duplicate connections) --- src/network/bmproto.py | 14 ++++++++++++++ src/network/tcp.py | 5 ++++- 2 files changed, 18 insertions(+), 1 deletion(-) diff --git a/src/network/bmproto.py b/src/network/bmproto.py index 47c6c858..21ec692c 100644 --- a/src/network/bmproto.py +++ b/src/network/bmproto.py @@ -497,6 +497,20 @@ class BMProto(AdvancedDispatcher, ObjectTracker): return False except: pass + if not self.isOutbound: + # incoming from a peer we're connected to as outbound, or server full + # report the same error to counter deanonymisation + if state.Peer(self.destination.host, self.peerNode.port) in \ + network.connectionpool.BMConnectionPool().inboundConnections or \ + len(network.connectionpool.BMConnectionPool().inboundConnections) + \ + len(network.connectionpool.BMConnectionPool().outboundConnections) > \ + BMConfigParser().safeGetInt("bitmessagesettings", "maxtotalconnections") + \ + BMConfigParser().safeGetInt("bitmessagesettings", "maxbootstrapconnections"): + self.append_write_buf(protocol.assembleErrorMessage(fatal=2, + errorText="Server full, please try again later.")) + logger.debug ("Closed connection to %s due to server full or duplicate inbound/outbound.", + str(self.destination)) + return False if network.connectionpool.BMConnectionPool().isAlreadyConnected(self.nonce): self.append_write_buf(protocol.assembleErrorMessage(fatal=2, errorText="I'm connected to myself. Closing connection.")) diff --git a/src/network/tcp.py b/src/network/tcp.py index 922aa79d..5a27aca3 100644 --- a/src/network/tcp.py +++ b/src/network/tcp.py @@ -292,7 +292,10 @@ class TCPServer(AdvancedDispatcher): if len(network.connectionpool.BMConnectionPool().inboundConnections) + \ len(network.connectionpool.BMConnectionPool().outboundConnections) > \ BMConfigParser().safeGetInt("bitmessagesettings", "maxtotalconnections") + \ - BMConfigParser().safeGetInt("bitmessagesettings", "maxbootstrapconnections"): + BMConfigParser().safeGetInt("bitmessagesettings", "maxbootstrapconnections") + 10: + # 10 is a sort of buffer, in between it will go through the version handshake + # and return an error to the peer + logger.warning("Server full, dropping connection") sock.close() return try: From 4086253730e2551a9b3aa3df5af13ff9be322bee Mon Sep 17 00:00:00 2001 From: Peter Surda Date: Tue, 2 Jan 2018 15:24:47 +0100 Subject: [PATCH 312/407] Bandwidth limit optimisation - should be slightly more accurate and use slightly fewer resources --- src/network/advanceddispatcher.py | 4 ++-- src/network/asyncore_pollchoose.py | 18 ++++++++++++++---- 2 files changed, 16 insertions(+), 6 deletions(-) diff --git a/src/network/advanceddispatcher.py b/src/network/advanceddispatcher.py index c50b6a43..3e84ed85 100644 --- a/src/network/advanceddispatcher.py +++ b/src/network/advanceddispatcher.py @@ -75,7 +75,7 @@ class AdvancedDispatcher(asyncore.dispatcher): def writable(self): self.uploadChunk = AdvancedDispatcher._buf_len if asyncore.maxUploadRate > 0: - self.uploadChunk = asyncore.uploadBucket + self.uploadChunk = int(asyncore.uploadBucket) self.uploadChunk = min(self.uploadChunk, len(self.write_buf)) return asyncore.dispatcher.writable(self) and \ (self.connecting or (self.connected and self.uploadChunk > 0)) @@ -83,7 +83,7 @@ class AdvancedDispatcher(asyncore.dispatcher): def readable(self): self.downloadChunk = AdvancedDispatcher._buf_len if asyncore.maxDownloadRate > 0: - self.downloadChunk = asyncore.downloadBucket + self.downloadChunk = int(asyncore.downloadBucket) try: if self.expectBytes > 0 and not self.fullyEstablished: self.downloadChunk = min(self.downloadChunk, self.expectBytes - len(self.read_buf)) diff --git a/src/network/asyncore_pollchoose.py b/src/network/asyncore_pollchoose.py index c5586a91..5717ff78 100644 --- a/src/network/asyncore_pollchoose.py +++ b/src/network/asyncore_pollchoose.py @@ -112,6 +112,8 @@ uploadBucket = 0 sentBytes = 0 def read(obj): + if not can_receive(): + return try: obj.handle_read_event() except _reraised_exceptions: @@ -120,6 +122,8 @@ def read(obj): obj.handle_error() def write(obj): + if not can_send(): + return try: obj.handle_write_event() except _reraised_exceptions: @@ -136,12 +140,18 @@ def set_rates(download, upload): downloadTimestamp = time.time() uploadTimestamp = time.time() +def can_receive(): + return maxDownloadRate == 0 or downloadBucket > 0 + +def can_send(): + return maxUploadRate == 0 or uploadBucket > 0 + def update_received(download=0): global receivedBytes, downloadBucket, downloadTimestamp currentTimestamp = time.time() receivedBytes += download if maxDownloadRate > 0: - bucketIncrease = int(maxDownloadRate * (currentTimestamp - downloadTimestamp)) + bucketIncrease = maxDownloadRate * (currentTimestamp - downloadTimestamp) downloadBucket += bucketIncrease if downloadBucket > maxDownloadRate: downloadBucket = int(maxDownloadRate) @@ -153,7 +163,7 @@ def update_sent(upload=0): currentTimestamp = time.time() sentBytes += upload if maxUploadRate > 0: - bucketIncrease = int(maxUploadRate * (currentTimestamp - uploadTimestamp)) + bucketIncrease = maxUploadRate * (currentTimestamp - uploadTimestamp) uploadBucket += bucketIncrease if uploadBucket > maxUploadRate: uploadBucket = int(maxUploadRate) @@ -170,9 +180,9 @@ def _exception(obj): def readwrite(obj, flags): try: - if flags & select.POLLIN: + if flags & select.POLLIN and can_receive(): obj.handle_read_event() - if flags & select.POLLOUT: + if flags & select.POLLOUT and can_send(): obj.handle_write_event() if flags & select.POLLPRI: obj.handle_expt_event() From f74f82e54f97a9075e95ae59474e4948ce1d6e82 Mon Sep 17 00:00:00 2001 From: Peter Surda Date: Tue, 2 Jan 2018 22:20:33 +0100 Subject: [PATCH 313/407] Start downloading earlier --- src/network/objectracker.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/network/objectracker.py b/src/network/objectracker.py index f846e7d5..62f01e4f 100644 --- a/src/network/objectracker.py +++ b/src/network/objectracker.py @@ -29,6 +29,7 @@ class ObjectTracker(object): invInitialCapacity = 50000 invErrorRate = 0.03 trackingExpires = 3600 + initialTimeOffset = 60 def __init__(self): self.objectsNewToMe = {} @@ -87,7 +88,7 @@ class ObjectTracker(object): if hashId in Dandelion().hashMap: Dandelion().fluffTrigger(hashId) if hashId not in missingObjects: - missingObjects[hashId] = time.time() + missingObjects[hashId] = time.time() - ObjectTracker.initialTimeOffset with self.objectsNewToMeLock: self.objectsNewToMe[hashId] = True From c9851b9f41da4db4365ef080b1533ab845992bd1 Mon Sep 17 00:00:00 2001 From: Peter Surda Date: Tue, 2 Jan 2018 22:23:03 +0100 Subject: [PATCH 314/407] Connection lookups invalid data handling - shouldn't throw an exception if argument is a string rather than Peer --- src/network/connectionpool.py | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/src/network/connectionpool.py b/src/network/connectionpool.py index 3b817e65..44534a76 100644 --- a/src/network/connectionpool.py +++ b/src/network/connectionpool.py @@ -65,12 +65,18 @@ class BMConnectionPool(object): def getConnectionByAddr(self, addr): if addr in self.inboundConnections: return self.inboundConnections[addr] - if addr.host in self.inboundConnections: - return self.inboundConnections[addr.host] + try: + if addr.host in self.inboundConnections: + return self.inboundConnections[addr.host] + except AttributeError: + pass if addr in self.outboundConnections: return self.outboundConnections[addr] - if addr.host in self.udpSockets: - return self.udpSockets[addr.host] + try: + if addr.host in self.udpSockets: + return self.udpSockets[addr.host] + except AttributeError: + pass raise KeyError def isAlreadyConnected(self, nodeid): From 1b921a718cc06ea7e49f4085650f60ac2c50e5dd Mon Sep 17 00:00:00 2001 From: Dmitri Bogomolov <4glitch@gmail.com> Date: Thu, 28 Sep 2017 18:10:03 +0300 Subject: [PATCH 315/407] Check daemon status in singleinstance instead of config --- src/helper_generic.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/helper_generic.py b/src/helper_generic.py index 4f7a1299..588ae8f1 100644 --- a/src/helper_generic.py +++ b/src/helper_generic.py @@ -6,7 +6,7 @@ from multiprocessing import current_process from threading import current_thread, enumerate import traceback -from bmconfigparser import BMConfigParser +import shared from debug import logger import queues import shutdown @@ -54,7 +54,7 @@ def signal_handler(signal, frame): if current_thread().name not in ("PyBitmessage", "MainThread"): return logger.error("Got signal %i", signal) - if BMConfigParser().safeGetBoolean('bitmessagesettings', 'daemon'): + if shared.thisapp.daemon: shutdown.doCleanShutdown() else: allThreadTraceback(frame) From 0c4d4de82fb8f57fd488beee35181b2a08c7e5e6 Mon Sep 17 00:00:00 2001 From: Dmitri Bogomolov <4glitch@gmail.com> Date: Sun, 1 Oct 2017 18:39:35 +0300 Subject: [PATCH 316/407] Changed the daemon check approach in other places, where it makes sense --- src/class_singleCleaner.py | 2 +- src/shutdown.py | 3 +-- src/tr.py | 4 ++-- 3 files changed, 4 insertions(+), 5 deletions(-) diff --git a/src/class_singleCleaner.py b/src/class_singleCleaner.py index bec1a11d..ed0ee044 100644 --- a/src/class_singleCleaner.py +++ b/src/class_singleCleaner.py @@ -66,7 +66,7 @@ class singleCleaner(threading.Thread, StoppableThread): # If we are running as a daemon then we are going to fill up the UI # queue which will never be handled by a UI. We should clear it to # save memory. - if BMConfigParser().safeGetBoolean('bitmessagesettings', 'daemon'): + if shared.thisapp.daemon: queues.UISignalQueue.queue.clear() if timeWeLastClearedInventoryAndPubkeysTables < int(time.time()) - 7380: timeWeLastClearedInventoryAndPubkeysTables = int(time.time()) diff --git a/src/shutdown.py b/src/shutdown.py index 49c2fb9b..f447148b 100644 --- a/src/shutdown.py +++ b/src/shutdown.py @@ -3,7 +3,6 @@ import Queue import threading import time -from bmconfigparser import BMConfigParser from debug import logger from helper_sql import sqlQuery, sqlStoredProcedure from helper_threading import StoppableThread @@ -60,7 +59,7 @@ def doCleanShutdown(): except Queue.Empty: break - if BMConfigParser().safeGetBoolean('bitmessagesettings','daemon'): + if shared.thisapp.daemon: logger.info('Clean shutdown complete.') shared.thisapp.cleanup() os._exit(0) diff --git a/src/tr.py b/src/tr.py index c185690c..19c234a1 100644 --- a/src/tr.py +++ b/src/tr.py @@ -1,6 +1,6 @@ import os -from bmconfigparser import BMConfigParser +import shared # This is used so that the translateText function can be used when we are in daemon mode and not using any QT functions. class translateClass: @@ -17,7 +17,7 @@ def _translate(context, text, disambiguation = None, encoding = None, n = None): return translateText(context, text, n) def translateText(context, text, n = None): - if not BMConfigParser().safeGetBoolean('bitmessagesettings', 'daemon'): + if not shared.thisapp.daemon: try: from PyQt4 import QtCore, QtGui except Exception as err: From 460107abaa05cf0bfb51098e68c868171a0d57b0 Mon Sep 17 00:00:00 2001 From: Dmitri Bogomolov <4glitch@gmail.com> Date: Sat, 7 Oct 2017 15:50:25 +0300 Subject: [PATCH 317/407] Handled AttributeError when tr imported from plugin --- src/tr.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/tr.py b/src/tr.py index 19c234a1..cf7f16ac 100644 --- a/src/tr.py +++ b/src/tr.py @@ -17,7 +17,11 @@ def _translate(context, text, disambiguation = None, encoding = None, n = None): return translateText(context, text, n) def translateText(context, text, n = None): - if not shared.thisapp.daemon: + try: + is_daemon = shared.thisapp.daemon + except AttributeError: # inside the plugin + is_daemon = False + if not is_daemon: try: from PyQt4 import QtCore, QtGui except Exception as err: From ba91d212614798a371c8a41233a5288c155c4d71 Mon Sep 17 00:00:00 2001 From: Peter Surda Date: Mon, 22 Jan 2018 22:18:01 +0100 Subject: [PATCH 318/407] CPU hogging fix - handle _command functions that don't return anything - fix udp command function --- src/network/advanceddispatcher.py | 2 +- src/network/udp.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/network/advanceddispatcher.py b/src/network/advanceddispatcher.py index 3e84ed85..31555c62 100644 --- a/src/network/advanceddispatcher.py +++ b/src/network/advanceddispatcher.py @@ -58,7 +58,7 @@ class AdvancedDispatcher(asyncore.dispatcher): break if len(self.read_buf) < self.expectBytes: return False - if getattr(self, "state_" + str(self.state))() is False: + if not getattr(self, "state_" + str(self.state))(): break except AttributeError: logger.error("Unknown state %s", self.state) diff --git a/src/network/udp.py b/src/network/udp.py index e7f6974d..0dba5a3f 100644 --- a/src/network/udp.py +++ b/src/network/udp.py @@ -61,7 +61,7 @@ class UDPSocket(BMProto): pass def state_bm_command(self): - BMProto.state_bm_command(self) + return BMProto.state_bm_command(self) # disable most commands before doing research / testing # only addr (peer discovery), error and object are implemented From 01c8f3b66d5c40e3d3fdda6f884bc76ea9193380 Mon Sep 17 00:00:00 2001 From: Peter Surda Date: Mon, 22 Jan 2018 22:37:29 +0100 Subject: [PATCH 319/407] Fix asyncore CPU usage on no connection - if there are no connections, asyncore would hog CPU - thanks to an anonymous contributor --- src/network/asyncore_pollchoose.py | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/network/asyncore_pollchoose.py b/src/network/asyncore_pollchoose.py index 5717ff78..de8ededc 100644 --- a/src/network/asyncore_pollchoose.py +++ b/src/network/asyncore_pollchoose.py @@ -52,6 +52,7 @@ import select import socket import sys import time +from threading import current_thread import warnings import os @@ -246,6 +247,8 @@ def select_poller(timeout=0.0, map=None): if obj is None: continue _exception(obj) + else: + current_thread().stop.wait(timeout) def poll_poller(timeout=0.0, map=None): """A poller which uses poll(), available on most UNIXen.""" @@ -294,6 +297,8 @@ def poll_poller(timeout=0.0, map=None): if obj is None: continue readwrite(obj, flags) + else: + current_thread().stop.wait(timeout) # Aliases for backward compatibility poll = select_poller @@ -349,6 +354,8 @@ def epoll_poller(timeout=0.0, map=None): if obj is None: continue readwrite(obj, flags) + else: + current_thread().stop.wait(timeout) def kqueue_poller(timeout=0.0, map=None): """A poller which uses kqueue(), BSD specific.""" @@ -383,6 +390,8 @@ def kqueue_poller(timeout=0.0, map=None): if event.filter == select.KQ_FILTER_WRITE: write(obj) kqueue.close() + else: + current_thread().stop.wait(timeout) def loop(timeout=30.0, use_poll=False, map=None, count=None, From d6df4470e1153fc01f32691cdff7bc415c73a4f7 Mon Sep 17 00:00:00 2001 From: Peter Surda Date: Tue, 23 Jan 2018 15:59:58 +0100 Subject: [PATCH 320/407] No connection CPU hog fix - the previous fix was incomplete, it shouldn't consume excessive resources now when there are no connections --- src/network/asyncore_pollchoose.py | 35 +++++++++++++----------------- 1 file changed, 15 insertions(+), 20 deletions(-) diff --git a/src/network/asyncore_pollchoose.py b/src/network/asyncore_pollchoose.py index de8ededc..f9dc9ee5 100644 --- a/src/network/asyncore_pollchoose.py +++ b/src/network/asyncore_pollchoose.py @@ -398,6 +398,8 @@ def loop(timeout=30.0, use_poll=False, map=None, count=None, poller=None): if map is None: map = socket_map + if count is None: + count = True # code which grants backward compatibility with "use_poll" # argument which should no longer be used in favor of # "poller" @@ -414,27 +416,20 @@ def loop(timeout=30.0, use_poll=False, map=None, count=None, elif hasattr(select, 'select'): poller = select_poller - if count is None: - while map: - # fill buckets first - update_sent() - update_received() - # then poll - poller(timeout, map) + if timeout == 0: + deadline = 0 else: - if timeout == 0: - deadline = 0 - else: - deadline = time.time() + timeout - while map and count > 0: - # fill buckets first - update_sent() - update_received() - subtimeout = deadline - time.time() - if subtimeout <= 0: - break - poller(subtimeout, map) - # then poll + deadline = time.time() + timeout + while count: + # fill buckets first + update_sent() + update_received() + subtimeout = deadline - time.time() + if subtimeout <= 0: + break + # then poll + poller(subtimeout, map) + if type(count) is int: count = count - 1 class dispatcher: From 870905100594bdf6c1ab37fbf7d2cd9a3fb38d4e Mon Sep 17 00:00:00 2001 From: Peter Surda Date: Thu, 25 Jan 2018 08:11:42 +0100 Subject: [PATCH 321/407] Fix non-ascii logging Tested with UTF-8, KOI8-R and ISO-8859-2 encodings. --- src/debug.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/debug.py b/src/debug.py index b03e8bf9..79c6e64e 100644 --- a/src/debug.py +++ b/src/debug.py @@ -54,7 +54,7 @@ def configureLogging(): 'version': 1, 'formatters': { 'default': { - 'format': '%(asctime)s - %(levelname)s - %(message)s', + 'format': u'%(asctime)s - %(levelname)s - %(message)s', }, }, 'handlers': { From 61ddc1208e21ed51510e09b3ef3ac8c6af37cd4f Mon Sep 17 00:00:00 2001 From: Dmitri Bogomolov <4glitch@gmail.com> Date: Thu, 25 Jan 2018 12:58:29 +0200 Subject: [PATCH 322/407] No more shared.daemon variable --- src/bitmessagemain.py | 2 -- src/class_singleCleaner.py | 2 +- src/shared.py | 2 +- 3 files changed, 2 insertions(+), 4 deletions(-) diff --git a/src/bitmessagemain.py b/src/bitmessagemain.py index 6f796939..74011f29 100755 --- a/src/bitmessagemain.py +++ b/src/bitmessagemain.py @@ -220,8 +220,6 @@ class Main: elif opt in ("-c", "--curses"): state.curses = True - shared.daemon = daemon - # is the application already running? If yes then exit. shared.thisapp = singleinstance("", daemon) diff --git a/src/class_singleCleaner.py b/src/class_singleCleaner.py index ed0ee044..1def6cdb 100644 --- a/src/class_singleCleaner.py +++ b/src/class_singleCleaner.py @@ -121,7 +121,7 @@ class singleCleaner(threading.Thread, StoppableThread): if "Errno 28" in str(err): logger.fatal('(while receiveDataThread knownnodes.needToWriteKnownNodesToDisk) Alert: Your disk or data storage volume is full. ') queues.UISignalQueue.put(('alert', (tr._translate("MainWindow", "Disk full"), tr._translate("MainWindow", 'Alert: Your disk or data storage volume is full. Bitmessage will now exit.'), True))) - if shared.daemon: + if shared.thisapp.daemon: os._exit(0) shared.needToWriteKnownNodesToDisk = False diff --git a/src/shared.py b/src/shared.py index 34597c78..e2f3c1cc 100644 --- a/src/shared.py +++ b/src/shared.py @@ -49,7 +49,7 @@ clientHasReceivedIncomingConnections = False #used by API command clientStatus numberOfMessagesProcessed = 0 numberOfBroadcastsProcessed = 0 numberOfPubkeysProcessed = 0 -daemon = False + needToWriteKnownNodesToDisk = False # If True, the singleCleaner will write it to disk eventually. maximumLengthOfTimeToBotherResendingMessages = 0 timeOffsetWrongCount = 0 From 0097ea6476a96aa49023d32f33b2277fbb364f59 Mon Sep 17 00:00:00 2001 From: Dmitri Bogomolov <4glitch@gmail.com> Date: Thu, 25 Jan 2018 14:29:25 +0200 Subject: [PATCH 323/407] The condition which is always False causes critical error #1081 --- src/bitmessageqt/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/bitmessageqt/__init__.py b/src/bitmessageqt/__init__.py index e6ff0cd0..5f69228d 100644 --- a/src/bitmessageqt/__init__.py +++ b/src/bitmessageqt/__init__.py @@ -2542,7 +2542,7 @@ class MyForm(settingsmixin.SMainWindow): tableWidget.item(i, 2).setUnread(False) tableWidget.item(i, 3).setFont(font) # sqlite default limit, unfortunately getting/setting isn't exposed to python - if i % 999 == 999: + if i % 999 == 0: markread += partialUpdate(self.getCurrentFolder(), msgids) msgids = [] From 80fdb08f0369a86e4e65b740c0457b467fdff416 Mon Sep 17 00:00:00 2001 From: Dmitri Bogomolov <4glitch@gmail.com> Date: Thu, 25 Jan 2018 23:04:38 +0200 Subject: [PATCH 324/407] Made use of helper_sql.sqlExecuteChunked() in the on_action_MarkAllRead --- src/bitmessageqt/__init__.py | 42 ++++++++++++++++-------------------- src/helper_sql.py | 21 ++++++++++++++---- 2 files changed, 35 insertions(+), 28 deletions(-) diff --git a/src/bitmessageqt/__init__.py b/src/bitmessageqt/__init__.py index 5f69228d..b43f8163 100644 --- a/src/bitmessageqt/__init__.py +++ b/src/bitmessageqt/__init__.py @@ -2509,48 +2509,42 @@ class MyForm(settingsmixin.SMainWindow): # self.rerenderInboxToLabels() def on_action_MarkAllRead(self): - def partialUpdate(folder, msgids): - if len(msgids) == 0: - return 0 - if folder == 'sent': - return sqlExecute( - "UPDATE sent SET read = 1 WHERE ackdata IN(%s) AND read=0" %(",".join("?"*len(msgids))), *msgids) - else: - return sqlExecute( - "UPDATE inbox SET read = 1 WHERE msgid IN(%s) AND read=0" %(",".join("?"*len(msgids))), *msgids) - - if QtGui.QMessageBox.question(self, "Marking all messages as read?", _translate("MainWindow", "Are you sure you would like to mark all messages read?"), QMessageBox.Yes|QMessageBox.No) != QMessageBox.Yes: + if QtGui.QMessageBox.question( + self, "Marking all messages as read?", + _translate( + "MainWindow", + "Are you sure you would like to mark all messages read?" + ), QMessageBox.Yes | QMessageBox.No) != QMessageBox.Yes: return addressAtCurrentRow = self.getCurrentAccount() tableWidget = self.getCurrentMessagelist() - if tableWidget.rowCount() == 0: + idCount = tableWidget.rowCount() + if idCount == 0: return - msgids = [] - font = QFont() font.setBold(False) - markread = 0 - - for i in range(0, tableWidget.rowCount()): + msgids = [] + for i in range(0, idCount): msgids.append(str(tableWidget.item( i, 3).data(Qt.UserRole).toPyObject())) tableWidget.item(i, 0).setUnread(False) tableWidget.item(i, 1).setUnread(False) tableWidget.item(i, 2).setUnread(False) tableWidget.item(i, 3).setFont(font) - # sqlite default limit, unfortunately getting/setting isn't exposed to python - if i % 999 == 0: - markread += partialUpdate(self.getCurrentFolder(), msgids) - msgids = [] - if len(msgids) > 0: - markread += partialUpdate(self.getCurrentFolder(), msgids) + markread = sqlExecuteChunked( + "UPDATE %s SET read = 1 WHERE %s IN({0}) AND read=0" % ( + ('sent', 'ackdata') if self.getCurrentFolder() == 'sent' + else ('inbox', 'msgid') + ), idCount, *msgids + ) if markread > 0: - self.propagateUnreadCount(addressAtCurrentRow, self.getCurrentFolder(), None, 0) + self.propagateUnreadCount( + addressAtCurrentRow, self.getCurrentFolder(), None, 0) def click_NewAddressDialog(self): addresses = [] diff --git a/src/helper_sql.py b/src/helper_sql.py index 7410202b..fec67bef 100644 --- a/src/helper_sql.py +++ b/src/helper_sql.py @@ -21,8 +21,10 @@ def sqlQuery(sqlStatement, *args): return queryreturn + def sqlExecuteChunked(sqlStatement, idCount, *args): - #SQLITE_MAX_VARIABLE_NUMBER, unfortunately getting/setting isn't exposed to python + # SQLITE_MAX_VARIABLE_NUMBER, + # unfortunately getting/setting isn't exposed to python sqlExecuteChunked.chunkSize = 999 if idCount == 0 or idCount > len(args): @@ -30,15 +32,26 @@ def sqlExecuteChunked(sqlStatement, idCount, *args): totalRowCount = 0 with sqlLock: - for i in range(len(args)-idCount, len(args), sqlExecuteChunked.chunkSize - (len(args)-idCount)): - sqlSubmitQueue.put(sqlStatement) + for i in range( + len(args) - idCount, len(args), + sqlExecuteChunked.chunkSize - (len(args) - idCount) + ): + chunk_slice = args[ + i:i+sqlExecuteChunked.chunkSize - (len(args) - idCount) + ] + sqlSubmitQueue.put( + sqlStatement.format(','.join('?' * len(chunk_slice))) + ) # first static args, and then iterative chunk - sqlSubmitQueue.put(args[0:len(args)-idCount] + args[i:i+sqlExecuteChunked.chunkSize - (len(args)-idCount)]) + sqlSubmitQueue.put( + args[0:len(args)-idCount] + chunk_slice + ) retVal = sqlReturnQueue.get() totalRowCount += retVal[1] sqlSubmitQueue.put('commit') return totalRowCount + def sqlExecute(sqlStatement, *args): sqlLock.acquire() sqlSubmitQueue.put(sqlStatement) From 6fca1631af41aa46f1b30543676b59c553ac9e29 Mon Sep 17 00:00:00 2001 From: Dmitri Bogomolov <4glitch@gmail.com> Date: Thu, 25 Jan 2018 23:22:15 +0200 Subject: [PATCH 325/407] Made use of helper_sql.sqlExecuteChunked() also in on_action_InboxMarkUnread --- src/bitmessageqt/__init__.py | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/src/bitmessageqt/__init__.py b/src/bitmessageqt/__init__.py index b43f8163..98c6dace 100644 --- a/src/bitmessageqt/__init__.py +++ b/src/bitmessageqt/__init__.py @@ -2784,9 +2784,13 @@ class MyForm(settingsmixin.SMainWindow): tableWidget.item(currentRow, 1).setUnread(True) tableWidget.item(currentRow, 2).setUnread(True) tableWidget.item(currentRow, 3).setFont(font) - #sqlite requires the exact number of ?s to prevent injection - rowcount = sqlExecute('''UPDATE inbox SET read=0 WHERE msgid IN (%s) AND read=1''' % ( - "?," * len(inventoryHashesToMarkUnread))[:-1], *inventoryHashesToMarkUnread) + # for 1081 + idCount = len(inventoryHashesToMarkUnread) + rowcount = sqlExecuteChunked( + '''UPDATE inbox SET read=0 WHERE msgid IN ({0}) AND read=1''', + idCount, *inventoryHashesToMarkUnread + ) + if rowcount == 1: # performance optimisation self.propagateUnreadCount(tableWidget.item(currentRow, 1 if tableWidget.item(currentRow, 1).type == AccountMixin.SUBSCRIPTION else 0).data(Qt.UserRole), self.getCurrentFolder()) From 0f61ed9f19940b2895c28eae986f5b085b6c83a9 Mon Sep 17 00:00:00 2001 From: Peter Surda Date: Sun, 28 Jan 2018 08:12:46 +0100 Subject: [PATCH 326/407] Optimise UI deleting and undeleting multiple messages - use sqlExecuteChunked and disable UI updates while removing entries --- src/bitmessageqt/__init__.py | 67 ++++++++++++++++++++---------------- 1 file changed, 37 insertions(+), 30 deletions(-) diff --git a/src/bitmessageqt/__init__.py b/src/bitmessageqt/__init__.py index 98c6dace..1ced3809 100644 --- a/src/bitmessageqt/__init__.py +++ b/src/bitmessageqt/__init__.py @@ -2985,54 +2985,61 @@ class MyForm(settingsmixin.SMainWindow): tableWidget = self.getCurrentMessagelist() if not tableWidget: return - unread = False currentRow = 0 folder = self.getCurrentFolder() shifted = QtGui.QApplication.queryKeyboardModifiers() & QtCore.Qt.ShiftModifier - while tableWidget.selectedIndexes(): - currentRow = tableWidget.selectedIndexes()[0].row() + tableWidget.setUpdatesEnabled(False); + inventoryHashesToTrash = [] + for row in tableWidget.selectedIndexes(): + currentRow = row.row() inventoryHashToTrash = str(tableWidget.item( - currentRow, 3).data(Qt.UserRole).toPyObject()) - if folder == "trash" or shifted: - sqlExecute('''DELETE FROM inbox WHERE msgid=?''', inventoryHashToTrash) - else: - sqlExecute('''UPDATE inbox SET folder='trash' WHERE msgid=?''', inventoryHashToTrash) - if tableWidget.item(currentRow, 0).unread: - self.propagateUnreadCount(tableWidget.item(currentRow, 1 if tableWidget.item(currentRow, 1).type == AccountMixin.SUBSCRIPTION else 0).data(Qt.UserRole), folder, self.getCurrentTreeWidget(), -1) - if folder != "trash" and not shifted: - self.propagateUnreadCount(tableWidget.item(currentRow, 1 if tableWidget.item(currentRow, 1).type == AccountMixin.SUBSCRIPTION else 0).data(Qt.UserRole), "trash", self.getCurrentTreeWidget(), 1) - - self.getCurrentMessageTextedit().setText("") + currentRow, 3).data(QtCore.Qt.UserRole).toPyObject()) + if inventoryHashToTrash in inventoryHashesToTrash: + continue + inventoryHashesToTrash.append(inventoryHashToTrash) tableWidget.removeRow(currentRow) - self.statusBar().showMessage(_translate( - "MainWindow", "Moved items to trash."), 10000) - if currentRow == 0: - tableWidget.selectRow(currentRow) + idCount = len(inventoryHashesToTrash) + if folder == "trash" or shifted: + sqlExecuteChunked('''DELETE FROM inbox WHERE msgid IN ({0})''', + idCount, *inventoryHashesToTrash) else: - tableWidget.selectRow(currentRow - 1) + sqlExecuteChunked('''UPDATE inbox SET folder='trash' WHERE msgid IN ({0})''', + idCount, *inventoryHashesToTrash) + tableWidget.selectRow(0 if currentRow == 0 else currentRow - 1) + self.getCurrentMessageTextedit().setText("") + tableWidget.setUpdatesEnabled(True) + self.propagateUnreadCount(self.getCurrentAccount, folder) + self.statusBar().showMessage(_translate( + "MainWindow", "Moved items to trash."), 10000) def on_action_TrashUndelete(self): tableWidget = self.getCurrentMessagelist() if not tableWidget: return - unread = False currentRow = 0 - while tableWidget.selectedIndexes(): - currentRow = tableWidget.selectedIndexes()[0].row() + tableWidget.setUpdatesEnabled(False) + inventoryHashesToTrash = [] + for row in tableWidget.selectedIndexes(): + currentRow = row.row() inventoryHashToTrash = str(tableWidget.item( - currentRow, 3).data(Qt.UserRole).toPyObject()) - sqlExecute('''UPDATE inbox SET folder='inbox' WHERE msgid=?''', inventoryHashToTrash) - if tableWidget.item(currentRow, 0).unread: - self.propagateUnreadCount(tableWidget.item(currentRow, 1 if tableWidget.item(currentRow, 1).type == AccountMixin.SUBSCRIPTION else 0).data(Qt.UserRole), "inbox", self.getCurrentTreeWidget(), 1) - self.propagateUnreadCount(tableWidget.item(currentRow, 1 if tableWidget.item(currentRow, 1).type == AccountMixin.SUBSCRIPTION else 0).data(Qt.UserRole), "trash", self.getCurrentTreeWidget(), -1) - self.getCurrentMessageTextedit().setText("") + currentRow, 3).data(QtCore.Qt.UserRole).toPyObject()) + if inventoryHashToTrash in inventoryHashesToTrash: + continue + inventoryHashesToTrash.append(inventoryHashToTrash) tableWidget.removeRow(currentRow) - self.statusBar().showMessage(_translate( - "MainWindow", "Undeleted item."), 10000) if currentRow == 0: tableWidget.selectRow(currentRow) else: tableWidget.selectRow(currentRow - 1) + idCount = len(inventoryHashesToTrash) + sqlExecuteChunked('''UPDATE inbox SET folder='inbox' WHERE msgid IN({0})''', + idCount, *inventoryHashesToTrash) + tableWidget.selectRow(0 if currentRow == 0 else currentRow - 1) + self.getCurrentMessageTextedit().setText("") + tableWidget.setUpdatesEnabled(True) + self.propagateUnreadCount(self.getCurrentAccount) + self.statusBar().showMessage(_translate( + "MainWindow", "Undeleted items."), 10000) def on_action_InboxSaveMessageAs(self): tableWidget = self.getCurrentMessagelist() From ff92cc30c81a7c58775eddda30e35583bed5cda3 Mon Sep 17 00:00:00 2001 From: Peter Surda Date: Sun, 28 Jan 2018 09:16:37 +0100 Subject: [PATCH 327/407] Fix previous commit (UI deleting optimisation) --- src/bitmessageqt/__init__.py | 34 ++++++++++++++++++---------------- 1 file changed, 18 insertions(+), 16 deletions(-) diff --git a/src/bitmessageqt/__init__.py b/src/bitmessageqt/__init__.py index 1ced3809..b3d19017 100644 --- a/src/bitmessageqt/__init__.py +++ b/src/bitmessageqt/__init__.py @@ -2990,14 +2990,15 @@ class MyForm(settingsmixin.SMainWindow): shifted = QtGui.QApplication.queryKeyboardModifiers() & QtCore.Qt.ShiftModifier tableWidget.setUpdatesEnabled(False); inventoryHashesToTrash = [] - for row in tableWidget.selectedIndexes(): - currentRow = row.row() - inventoryHashToTrash = str(tableWidget.item( - currentRow, 3).data(QtCore.Qt.UserRole).toPyObject()) - if inventoryHashToTrash in inventoryHashesToTrash: - continue - inventoryHashesToTrash.append(inventoryHashToTrash) - tableWidget.removeRow(currentRow) + # ranges in reversed order + for r in sorted(tableWidget.selectedRanges(), key=lambda r: r.topRow())[::-1]: + for i in range(r.bottomRow()-r.topRow()+1): + inventoryHashToTrash = str(tableWidget.item( + r.topRow()+i, 3).data(QtCore.Qt.UserRole).toPyObject()) + if inventoryHashToTrash in inventoryHashesToTrash: + continue + inventoryHashesToTrash.append(inventoryHashToTrash) + tableWidget.model().removeRows(r.topRow(), r.bottomRow()-r.topRow()+1) idCount = len(inventoryHashesToTrash) if folder == "trash" or shifted: sqlExecuteChunked('''DELETE FROM inbox WHERE msgid IN ({0})''', @@ -3019,14 +3020,15 @@ class MyForm(settingsmixin.SMainWindow): currentRow = 0 tableWidget.setUpdatesEnabled(False) inventoryHashesToTrash = [] - for row in tableWidget.selectedIndexes(): - currentRow = row.row() - inventoryHashToTrash = str(tableWidget.item( - currentRow, 3).data(QtCore.Qt.UserRole).toPyObject()) - if inventoryHashToTrash in inventoryHashesToTrash: - continue - inventoryHashesToTrash.append(inventoryHashToTrash) - tableWidget.removeRow(currentRow) + # ranges in reversed order + for r in sorted(tableWidget.selectedRanges(), key=lambda r: r.topRow())[::-1]: + for i in range(r.bottomRow()-r.topRow()+1): + inventoryHashToTrash = str(tableWidget.item( + r.topRow()+i, 3).data(QtCore.Qt.UserRole).toPyObject()) + if inventoryHashToTrash in inventoryHashesToTrash: + continue + inventoryHashesToTrash.append(inventoryHashToTrash) + tableWidget.model().removeRows(r.topRow(), r.bottomRow()-r.topRow()+1) if currentRow == 0: tableWidget.selectRow(currentRow) else: From aabce6bab2e703d713aa19369a51275ec86f03bf Mon Sep 17 00:00:00 2001 From: Peter Surda Date: Sun, 28 Jan 2018 12:48:25 +0100 Subject: [PATCH 328/407] Fix selection after delete/undelete --- src/bitmessageqt/__init__.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/bitmessageqt/__init__.py b/src/bitmessageqt/__init__.py index 65b9f5db..ff44bdd4 100644 --- a/src/bitmessageqt/__init__.py +++ b/src/bitmessageqt/__init__.py @@ -3000,6 +3000,7 @@ class MyForm(settingsmixin.SMainWindow): if inventoryHashToTrash in inventoryHashesToTrash: continue inventoryHashesToTrash.append(inventoryHashToTrash) + currentRow = r.topRow() tableWidget.model().removeRows(r.topRow(), r.bottomRow()-r.topRow()+1) idCount = len(inventoryHashesToTrash) if folder == "trash" or shifted: @@ -3030,6 +3031,7 @@ class MyForm(settingsmixin.SMainWindow): if inventoryHashToTrash in inventoryHashesToTrash: continue inventoryHashesToTrash.append(inventoryHashToTrash) + currentRow = r.topRow() tableWidget.model().removeRows(r.topRow(), r.bottomRow()-r.topRow()+1) if currentRow == 0: tableWidget.selectRow(currentRow) From cc63c1270bbfe5cdc33ae5705de68d5f0700a546 Mon Sep 17 00:00:00 2001 From: Peter Surda Date: Sun, 28 Jan 2018 16:13:07 +0100 Subject: [PATCH 329/407] Fix message content display after deleting/undeleting --- src/bitmessageqt/__init__.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/bitmessageqt/__init__.py b/src/bitmessageqt/__init__.py index ff44bdd4..10fae547 100644 --- a/src/bitmessageqt/__init__.py +++ b/src/bitmessageqt/__init__.py @@ -3001,6 +3001,7 @@ class MyForm(settingsmixin.SMainWindow): continue inventoryHashesToTrash.append(inventoryHashToTrash) currentRow = r.topRow() + self.getCurrentMessageTextedit().setText("") tableWidget.model().removeRows(r.topRow(), r.bottomRow()-r.topRow()+1) idCount = len(inventoryHashesToTrash) if folder == "trash" or shifted: @@ -3010,7 +3011,6 @@ class MyForm(settingsmixin.SMainWindow): sqlExecuteChunked('''UPDATE inbox SET folder='trash' WHERE msgid IN ({0})''', idCount, *inventoryHashesToTrash) tableWidget.selectRow(0 if currentRow == 0 else currentRow - 1) - self.getCurrentMessageTextedit().setText("") tableWidget.setUpdatesEnabled(True) self.propagateUnreadCount(self.getCurrentAccount, folder) self.statusBar().showMessage(_translate( @@ -3032,6 +3032,7 @@ class MyForm(settingsmixin.SMainWindow): continue inventoryHashesToTrash.append(inventoryHashToTrash) currentRow = r.topRow() + self.getCurrentMessageTextedit().setText("") tableWidget.model().removeRows(r.topRow(), r.bottomRow()-r.topRow()+1) if currentRow == 0: tableWidget.selectRow(currentRow) @@ -3041,7 +3042,6 @@ class MyForm(settingsmixin.SMainWindow): sqlExecuteChunked('''UPDATE inbox SET folder='inbox' WHERE msgid IN({0})''', idCount, *inventoryHashesToTrash) tableWidget.selectRow(0 if currentRow == 0 else currentRow - 1) - self.getCurrentMessageTextedit().setText("") tableWidget.setUpdatesEnabled(True) self.propagateUnreadCount(self.getCurrentAccount) self.statusBar().showMessage(_translate( From 25ef9fec51a6a95eb2753309f066c9fb201d191c Mon Sep 17 00:00:00 2001 From: Dmitri Bogomolov <4glitch@gmail.com> Date: Wed, 24 Jan 2018 18:37:05 +0200 Subject: [PATCH 330/407] Added menu entry "File -> Go offline/Go online" switching dontconnect config variable and "Work offline" option to connectDialog. --- src/bitmessageqt/__init__.py | 13 ++++++++++++- src/bitmessageqt/bitmessageui.py | 13 +++++++++++++ src/bitmessageqt/connect.py | 8 ++++++-- 3 files changed, 31 insertions(+), 3 deletions(-) diff --git a/src/bitmessageqt/__init__.py b/src/bitmessageqt/__init__.py index 10fae547..f0ac0f5d 100644 --- a/src/bitmessageqt/__init__.py +++ b/src/bitmessageqt/__init__.py @@ -143,6 +143,8 @@ class MyForm(settingsmixin.SMainWindow): def init_file_menu(self): QtCore.QObject.connect(self.ui.actionExit, QtCore.SIGNAL( "triggered()"), self.quit) + QtCore.QObject.connect(self.ui.actionNetworkSwitch, QtCore.SIGNAL( + "triggered()"), self.network_switch) QtCore.QObject.connect(self.ui.actionManageKeys, QtCore.SIGNAL( "triggered()"), self.click_actionManageKeys) QtCore.QObject.connect(self.ui.actionDeleteAllTrashedMessages, @@ -1471,7 +1473,7 @@ class MyForm(settingsmixin.SMainWindow): BMConfigParser().remove_option( 'bitmessagesettings', 'dontconnect') BMConfigParser().save() - else: + elif self.connectDialogInstance.ui.radioButtonConfigureNetwork.isChecked(): self.click_actionSettings() def showMigrationWizard(self, level): @@ -2585,6 +2587,14 @@ class MyForm(settingsmixin.SMainWindow): else: logger.debug('new address dialog box rejected') + def network_switch(self): + dontconnect_option = not BMConfigParser().safeGetBoolean( + 'bitmessagesettings', 'dontconnect') + BMConfigParser().set( + 'bitmessagesettings', 'dontconnect', str(dontconnect_option)) + BMConfigParser().save() + self.ui.updateNetworkSwitchMenuLabel(dontconnect_option) + # Quit selected from menu or application indicator def quit(self): '''quit_msg = "Are you sure you want to exit Bitmessage?" @@ -4446,6 +4456,7 @@ def run(): 'bitmessagesettings', 'dontconnect') if myapp._firstrun: myapp.showConnectDialog() # ask the user if we may connect + myapp.ui.updateNetworkSwitchMenuLabel() # try: # if BMConfigParser().get('bitmessagesettings', 'mailchuck') < 1: diff --git a/src/bitmessageqt/bitmessageui.py b/src/bitmessageqt/bitmessageui.py index f001487e..3eb04101 100644 --- a/src/bitmessageqt/bitmessageui.py +++ b/src/bitmessageqt/bitmessageui.py @@ -586,6 +586,8 @@ class Ui_MainWindow(object): icon = QtGui.QIcon.fromTheme(_fromUtf8("dialog-password")) self.actionManageKeys.setIcon(icon) self.actionManageKeys.setObjectName(_fromUtf8("actionManageKeys")) + self.actionNetworkSwitch = QtGui.QAction(MainWindow) + self.actionNetworkSwitch.setObjectName(_fromUtf8("actionNetworkSwitch")) self.actionExit = QtGui.QAction(MainWindow) icon = QtGui.QIcon.fromTheme(_fromUtf8("application-exit")) self.actionExit.setIcon(icon) @@ -621,6 +623,7 @@ class Ui_MainWindow(object): self.menuFile.addAction(self.actionManageKeys) self.menuFile.addAction(self.actionDeleteAllTrashedMessages) self.menuFile.addAction(self.actionRegenerateDeterministicAddresses) + self.menuFile.addAction(self.actionNetworkSwitch) self.menuFile.addAction(self.actionExit) self.menuSettings.addAction(self.actionSettings) self.menuHelp.addAction(self.actionHelp) @@ -641,6 +644,16 @@ class Ui_MainWindow(object): MainWindow.setTabOrder(self.lineEditSubject, self.textEditMessage) MainWindow.setTabOrder(self.textEditMessage, self.pushButtonAddSubscription) + def updateNetworkSwitchMenuLabel(self, dontconnect=None): + if dontconnect is None: + dontconnect = BMConfigParser().safeGetBoolean( + 'bitmessagesettings', 'dontconnect') + self.actionNetworkSwitch.setText( + _translate("MainWindow", "Go online", None) + if dontconnect else + _translate("MainWindow", "Go offline", None) + ) + def retranslateUi(self, MainWindow): MainWindow.setWindowTitle(_translate("MainWindow", "Bitmessage", None)) self.treeWidgetYourIdentities.headerItem().setText(0, _translate("MainWindow", "Identities", None)) diff --git a/src/bitmessageqt/connect.py b/src/bitmessageqt/connect.py index 1e224afb..9151156f 100644 --- a/src/bitmessageqt/connect.py +++ b/src/bitmessageqt/connect.py @@ -39,13 +39,16 @@ class Ui_connectDialog(object): self.radioButtonConfigureNetwork = QtGui.QRadioButton(connectDialog) self.radioButtonConfigureNetwork.setObjectName(_fromUtf8("radioButtonConfigureNetwork")) self.gridLayout.addWidget(self.radioButtonConfigureNetwork, 2, 0, 1, 2) + self.radioButtonWorkOffline = QtGui.QRadioButton(connectDialog) + self.radioButtonWorkOffline.setObjectName(_fromUtf8("radioButtonWorkOffline")) + self.gridLayout.addWidget(self.radioButtonWorkOffline, 3, 0, 1, 2) spacerItem = QtGui.QSpacerItem(185, 24, QtGui.QSizePolicy.Expanding, QtGui.QSizePolicy.Minimum) - self.gridLayout.addItem(spacerItem, 3, 0, 1, 1) + self.gridLayout.addItem(spacerItem, 4, 0, 1, 1) self.buttonBox = QtGui.QDialogButtonBox(connectDialog) self.buttonBox.setOrientation(QtCore.Qt.Horizontal) self.buttonBox.setStandardButtons(QtGui.QDialogButtonBox.Ok) self.buttonBox.setObjectName(_fromUtf8("buttonBox")) - self.gridLayout.addWidget(self.buttonBox, 3, 1, 1, 1) + self.gridLayout.addWidget(self.buttonBox, 4, 1, 1, 1) self.retranslateUi(connectDialog) QtCore.QObject.connect(self.buttonBox, QtCore.SIGNAL(_fromUtf8("accepted()")), connectDialog.accept) @@ -57,4 +60,5 @@ class Ui_connectDialog(object): self.label.setText(_translate("connectDialog", "Bitmessage won\'t connect to anyone until you let it. ", None)) self.radioButtonConnectNow.setText(_translate("connectDialog", "Connect now", None)) self.radioButtonConfigureNetwork.setText(_translate("connectDialog", "Let me configure special network settings first", None)) + self.radioButtonWorkOffline.setText(_translate("connectDialog", "Work offline", None)) From 9e79386595bd7f8c2fa43b837e5afd1ca5b85fc5 Mon Sep 17 00:00:00 2001 From: Dmitri Bogomolov <4glitch@gmail.com> Date: Tue, 30 Jan 2018 13:55:01 +0200 Subject: [PATCH 331/407] Set state to "close" and call `handle_close()` for all connections --- src/network/connectionpool.py | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/network/connectionpool.py b/src/network/connectionpool.py index 44534a76..793e284f 100644 --- a/src/network/connectionpool.py +++ b/src/network/connectionpool.py @@ -203,6 +203,14 @@ class BMConnectionPool(object): continue self.lastSpawned = time.time() + else: + for i in ( + self.inboundConnections.values() + + self.outboundConnections.values() + ): + i.set_state("close") + # FIXME: rating will be increased after next connection + i.handle_close() if acceptConnections: if not self.listeningSockets: From 81c80aa98ff4404c0cd3762c769f1ed16863e7c1 Mon Sep 17 00:00:00 2001 From: Dmitri Bogomolov <4glitch@gmail.com> Date: Fri, 6 Oct 2017 14:45:27 +0300 Subject: [PATCH 332/407] Use AddAddressDialog for "Add sender to your Addres Book" context menu --- src/bitmessageqt/__init__.py | 47 +++++++++++++++++------------------- 1 file changed, 22 insertions(+), 25 deletions(-) diff --git a/src/bitmessageqt/__init__.py b/src/bitmessageqt/__init__.py index f0ac0f5d..5e2ec982 100644 --- a/src/bitmessageqt/__init__.py +++ b/src/bitmessageqt/__init__.py @@ -2138,25 +2138,31 @@ class MyForm(settingsmixin.SMainWindow): self.statusBar().showMessage(_translate( "MainWindow", "Sending email gateway registration request"), 10000) - def click_pushButtonAddAddressBook(self): - self.AddAddressDialogInstance = AddAddressDialog(self) - if self.AddAddressDialogInstance.exec_(): - if self.AddAddressDialogInstance.ui.labelAddressCheck.text() == _translate("MainWindow", "Address is valid."): + def click_pushButtonAddAddressBook(self, dialog=None): + if not dialog: + dialog = AddAddressDialog(self) + if dialog.exec_(): + if dialog.ui.labelAddressCheck.text() == \ + _translate("MainWindow", "Address is valid."): # First we must check to see if the address is already in the # address book. The user cannot add it again or else it will # cause problems when updating and deleting the entry. - address = addBMIfNotPresent(str( - self.AddAddressDialogInstance.ui.lineEditAddress.text())) - label = self.AddAddressDialogInstance.ui.newAddressLabel.text().toUtf8() - self.addEntryToAddressBook(address,label) + address = addBMIfNotPresent( + str(dialog.ui.lineEditAddress.text())) + label = str(dialog.ui.newAddressLabel.text().toUtf8()) + self.addEntryToAddressBook(address, label) else: self.statusBar().showMessage(_translate( - "MainWindow", "The address you entered was invalid. Ignoring it."), 10000) + "MainWindow", + "The address you entered was invalid. Ignoring it." + ), 10000) - def addEntryToAddressBook(self,address,label): - queryreturn = sqlQuery('''select * from addressbook where address=?''', address) + def addEntryToAddressBook(self, address, label): + queryreturn = sqlQuery( + '''select * from addressbook where address=?''', address) if queryreturn == []: - sqlExecute('''INSERT INTO addressbook VALUES (?,?)''', str(label), address) + sqlExecute('''INSERT INTO addressbook VALUES (?,?)''', + label, address) self.rerenderMessagelistFromLabels() self.rerenderMessagelistToLabels() self.rerenderAddressBook() @@ -2935,19 +2941,10 @@ class MyForm(settingsmixin.SMainWindow): # tableWidget.item(currentRow,1).data(Qt.UserRole).toPyObject() addressAtCurrentInboxRow = tableWidget.item( currentInboxRow, 1).data(Qt.UserRole) - # Let's make sure that it isn't already in the address book - queryreturn = sqlQuery('''select * from addressbook where address=?''', - addressAtCurrentInboxRow) - if queryreturn == []: - sqlExecute('''INSERT INTO addressbook VALUES (?,?)''', - '--New entry. Change label in Address Book.--', - addressAtCurrentInboxRow) - self.rerenderAddressBook() - self.statusBar().showMessage(_translate( - "MainWindow", "Entry added to the Address Book. Edit the label to your liking."), 10000) - else: - self.statusBar().showMessage(_translate( - "MainWindow", "Error: You cannot add the same address to your address book twice. Try renaming the existing one if you want."), 10000) + self.ui.tabWidget.setCurrentIndex(1) + dialog = AddAddressDialog(self) + dialog.ui.lineEditAddress.setText(addressAtCurrentInboxRow) + self.click_pushButtonAddAddressBook(dialog) def on_action_InboxAddSenderToBlackList(self): tableWidget = self.getCurrentMessagelist() From d9d9f9d7874117e75e5f90763b880adcca3b1d91 Mon Sep 17 00:00:00 2001 From: Dmitri Bogomolov <4glitch@gmail.com> Date: Thu, 12 Oct 2017 23:36:47 +0300 Subject: [PATCH 333/407] Returned AddAddressDialog and NewSubscriptionDialog into dialogs module where it's been initialized by loading the ui-files. --- src/bitmessageqt/__init__.py | 171 ++++++++-------------- src/bitmessageqt/addaddressdialog.py | 65 -------- src/bitmessageqt/addaddressdialog.ui | 25 +++- src/bitmessageqt/dialogs.py | 115 +++++++++++---- src/bitmessageqt/newsubscriptiondialog.py | 69 --------- src/bitmessageqt/newsubscriptiondialog.ui | 31 +++- 6 files changed, 200 insertions(+), 276 deletions(-) delete mode 100644 src/bitmessageqt/addaddressdialog.py delete mode 100644 src/bitmessageqt/newsubscriptiondialog.py diff --git a/src/bitmessageqt/__init__.py b/src/bitmessageqt/__init__.py index 5e2ec982..10cbc8f9 100644 --- a/src/bitmessageqt/__init__.py +++ b/src/bitmessageqt/__init__.py @@ -27,7 +27,6 @@ from newaddresswizard import * from messageview import MessageView from migrationwizard import * from foldertree import * -from newsubscriptiondialog import * from regenerateaddresses import * from newchandialog import * from safehtmlparser import * @@ -51,14 +50,14 @@ import debug import random import string from datetime import datetime, timedelta -from helper_sql import * from helper_ackPayload import genAckPayload +from helper_sql import sqlQuery, sqlExecute, sqlExecuteChunked, sqlStoredProcedure import helper_search import l10n import openclpow from utils import str_broadcast_subscribers, avatarize from account import * -from dialogs import AddAddressDialog +import dialogs from helper_generic import powQueueSize from inventory import ( Inventory, PendingDownloadQueue, PendingUpload, @@ -2140,71 +2139,85 @@ class MyForm(settingsmixin.SMainWindow): def click_pushButtonAddAddressBook(self, dialog=None): if not dialog: - dialog = AddAddressDialog(self) + dialog = dialogs.AddAddressDialog(self) if dialog.exec_(): - if dialog.ui.labelAddressCheck.text() == \ - _translate("MainWindow", "Address is valid."): - # First we must check to see if the address is already in the - # address book. The user cannot add it again or else it will - # cause problems when updating and deleting the entry. - address = addBMIfNotPresent( - str(dialog.ui.lineEditAddress.text())) - label = str(dialog.ui.newAddressLabel.text().toUtf8()) - self.addEntryToAddressBook(address, label) - else: + if not dialog.valid: self.statusBar().showMessage(_translate( "MainWindow", "The address you entered was invalid. Ignoring it." - ), 10000) + ), 10000) + return + + address = addBMIfNotPresent(str(dialog.lineEditAddress.text())) + # First we must check to see if the address is already in the + # address book. The user cannot add it again or else it will + # cause problems when updating and deleting the entry. + if shared.isAddressInMyAddressBook(address): + self.statusBar().showMessage(_translate( + "MainWindow", + "Error: You cannot add the same address to your" + " address book twice. Try renaming the existing one" + " if you want." + ), 10000) + return + label = str(dialog.lineEditLabel.text().toUtf8()) + self.addEntryToAddressBook(address, label) def addEntryToAddressBook(self, address, label): - queryreturn = sqlQuery( - '''select * from addressbook where address=?''', address) - if queryreturn == []: - sqlExecute('''INSERT INTO addressbook VALUES (?,?)''', - label, address) - self.rerenderMessagelistFromLabels() - self.rerenderMessagelistToLabels() - self.rerenderAddressBook() - else: - self.statusBar().showMessage(_translate( - "MainWindow", "Error: You cannot add the same address to your address book twice. Try renaming the existing one if you want."), 10000) + if shared.isAddressInMyAddressBook(address): + return + sqlExecute('''INSERT INTO addressbook VALUES (?,?)''', label, address) + self.rerenderMessagelistFromLabels() + self.rerenderMessagelistToLabels() + self.rerenderAddressBook() def addSubscription(self, address, label): - address = addBMIfNotPresent(address) - #This should be handled outside of this function, for error displaying and such, but it must also be checked here. + # This should be handled outside of this function, for error displaying + # and such, but it must also be checked here. if shared.isAddressInMySubscriptionsList(address): return - #Add to database (perhaps this should be separated from the MyForm class) - sqlExecute('''INSERT INTO subscriptions VALUES (?,?,?)''',str(label),address,True) + # Add to database (perhaps this should be separated from the MyForm class) + sqlExecute( + '''INSERT INTO subscriptions VALUES (?,?,?)''', + label, address, True + ) self.rerenderMessagelistFromLabels() shared.reloadBroadcastSendersForWhichImWatching() self.rerenderAddressBook() self.rerenderTabTreeSubscriptions() def click_pushButtonAddSubscription(self): - self.NewSubscriptionDialogInstance = NewSubscriptionDialog(self) - if self.NewSubscriptionDialogInstance.exec_(): - if self.NewSubscriptionDialogInstance.ui.labelAddressCheck.text() != _translate("MainWindow", "Address is valid."): - self.statusBar().showMessage(_translate("MainWindow", "The address you entered was invalid. Ignoring it."), 10000) + dialog = dialogs.NewSubscriptionDialog(self) + if dialog.exec_(): + if not dialog.valid: + self.statusBar().showMessage(_translate( + "MainWindow", + "The address you entered was invalid. Ignoring it." + ), 10000) return - address = addBMIfNotPresent(str(self.NewSubscriptionDialogInstance.ui.lineEditSubscriptionAddress.text())) - # We must check to see if the address is already in the subscriptions list. The user cannot add it again or else it will cause problems when updating and deleting the entry. + + address = addBMIfNotPresent(str(dialog.lineEditAddress.text())) + # We must check to see if the address is already in the + # subscriptions list. The user cannot add it again or else it + # will cause problems when updating and deleting the entry. if shared.isAddressInMySubscriptionsList(address): - self.statusBar().showMessage(_translate("MainWindow", "Error: You cannot add the same address to your subscriptions twice. Perhaps rename the existing one if you want."), 10000) + self.statusBar().showMessage(_translate( + "MainWindow", + "Error: You cannot add the same address to your" + " subscriptions twice. Perhaps rename the existing one" + " if you want." + ), 10000) return - label = self.NewSubscriptionDialogInstance.ui.newsubscriptionlabel.text().toUtf8() + label = str(dialog.lineEditLabel.text().toUtf8()) self.addSubscription(address, label) - # Now, if the user wants to display old broadcasts, let's get them out of the inventory and put them - # in the objectProcessorQueue to be processed - if self.NewSubscriptionDialogInstance.ui.checkBoxDisplayMessagesAlreadyInInventory.isChecked(): - status, addressVersion, streamNumber, ripe = decodeAddress(address) - Inventory().flush() - doubleHashOfAddressData = hashlib.sha512(hashlib.sha512(encodeVarint( - addressVersion) + encodeVarint(streamNumber) + ripe).digest()).digest() - tag = doubleHashOfAddressData[32:] - for value in Inventory().by_type_and_tag(3, tag): - queues.objectProcessorQueue.put((value.type, value.payload)) + # Now, if the user wants to display old broadcasts, let's get + # them out of the inventory and put them + # to the objectProcessorQueue to be processed + if dialog.checkBoxDisplayMessagesAlreadyInInventory.isChecked(): + for value in dialog.recent: + queues.objectProcessorQueue.put(( + value.type, value.payload + )) def click_pushButtonStatusIcon(self): logger.debug('click_pushButtonStatusIcon') @@ -2942,8 +2955,8 @@ class MyForm(settingsmixin.SMainWindow): addressAtCurrentInboxRow = tableWidget.item( currentInboxRow, 1).data(Qt.UserRole) self.ui.tabWidget.setCurrentIndex(1) - dialog = AddAddressDialog(self) - dialog.ui.lineEditAddress.setText(addressAtCurrentInboxRow) + dialog = dialogs.AddAddressDialog(self) + dialog.lineEditAddress.setText(addressAtCurrentInboxRow) self.click_pushButtonAddAddressBook(dialog) def on_action_InboxAddSenderToBlackList(self): @@ -4280,64 +4293,6 @@ class EmailGatewayRegistrationDialog(QtGui.QDialog): QtGui.QWidget.resize(self, QtGui.QWidget.sizeHint(self)) -class NewSubscriptionDialog(QtGui.QDialog): - - def __init__(self, parent): - QtGui.QWidget.__init__(self, parent) - self.ui = Ui_NewSubscriptionDialog() - self.ui.setupUi(self) - self.parent = parent - QtCore.QObject.connect(self.ui.lineEditSubscriptionAddress, QtCore.SIGNAL( - "textChanged(QString)"), self.addressChanged) - self.ui.checkBoxDisplayMessagesAlreadyInInventory.setText( - _translate("MainWindow", "Enter an address above.")) - - def addressChanged(self, QString): - self.ui.checkBoxDisplayMessagesAlreadyInInventory.setEnabled(False) - self.ui.checkBoxDisplayMessagesAlreadyInInventory.setChecked(False) - status, addressVersion, streamNumber, ripe = decodeAddress(str(QString)) - if status == 'missingbm': - self.ui.labelAddressCheck.setText(_translate( - "MainWindow", "The address should start with ''BM-''")) - elif status == 'checksumfailed': - self.ui.labelAddressCheck.setText(_translate( - "MainWindow", "The address is not typed or copied correctly (the checksum failed).")) - elif status == 'versiontoohigh': - self.ui.labelAddressCheck.setText(_translate( - "MainWindow", "The version number of this address is higher than this software can support. Please upgrade Bitmessage.")) - elif status == 'invalidcharacters': - self.ui.labelAddressCheck.setText(_translate( - "MainWindow", "The address contains invalid characters.")) - elif status == 'ripetooshort': - self.ui.labelAddressCheck.setText(_translate( - "MainWindow", "Some data encoded in the address is too short.")) - elif status == 'ripetoolong': - self.ui.labelAddressCheck.setText(_translate( - "MainWindow", "Some data encoded in the address is too long.")) - elif status == 'varintmalformed': - self.ui.labelAddressCheck.setText(_translate( - "MainWindow", "Some data encoded in the address is malformed.")) - elif status == 'success': - self.ui.labelAddressCheck.setText( - _translate("MainWindow", "Address is valid.")) - if addressVersion <= 3: - self.ui.checkBoxDisplayMessagesAlreadyInInventory.setText( - _translate("MainWindow", "Address is an old type. We cannot display its past broadcasts.")) - else: - Inventory().flush() - doubleHashOfAddressData = hashlib.sha512(hashlib.sha512(encodeVarint( - addressVersion) + encodeVarint(streamNumber) + ripe).digest()).digest() - tag = doubleHashOfAddressData[32:] - count = len(Inventory().by_type_and_tag(3, tag)) - if count == 0: - self.ui.checkBoxDisplayMessagesAlreadyInInventory.setText( - _translate("MainWindow", "There are no recent broadcasts from this address to display.")) - else: - self.ui.checkBoxDisplayMessagesAlreadyInInventory.setEnabled(True) - self.ui.checkBoxDisplayMessagesAlreadyInInventory.setText( - _translate("MainWindow", "Display the %1 recent broadcast(s) from this address.").arg(count)) - - class NewAddressDialog(QtGui.QDialog): def __init__(self, parent): diff --git a/src/bitmessageqt/addaddressdialog.py b/src/bitmessageqt/addaddressdialog.py deleted file mode 100644 index 5ed19e0a..00000000 --- a/src/bitmessageqt/addaddressdialog.py +++ /dev/null @@ -1,65 +0,0 @@ -# -*- coding: utf-8 -*- - -# Form implementation generated from reading ui file 'addaddressdialog.ui' -# -# Created: Sat Nov 30 20:35:38 2013 -# by: PyQt4 UI code generator 4.10.3 -# -# WARNING! All changes made in this file will be lost! - -from PyQt4 import QtCore, QtGui - -try: - _fromUtf8 = QtCore.QString.fromUtf8 -except AttributeError: - def _fromUtf8(s): - return s - -try: - _encoding = QtGui.QApplication.UnicodeUTF8 - def _translate(context, text, disambig): - return QtGui.QApplication.translate(context, text, disambig, _encoding) -except AttributeError: - def _translate(context, text, disambig): - return QtGui.QApplication.translate(context, text, disambig) - -class Ui_AddAddressDialog(object): - def setupUi(self, AddAddressDialog): - AddAddressDialog.setObjectName(_fromUtf8("AddAddressDialog")) - AddAddressDialog.resize(368, 162) - self.formLayout = QtGui.QFormLayout(AddAddressDialog) - self.formLayout.setFieldGrowthPolicy(QtGui.QFormLayout.AllNonFixedFieldsGrow) - self.formLayout.setObjectName(_fromUtf8("formLayout")) - self.label_2 = QtGui.QLabel(AddAddressDialog) - self.label_2.setObjectName(_fromUtf8("label_2")) - self.formLayout.setWidget(0, QtGui.QFormLayout.SpanningRole, self.label_2) - self.newAddressLabel = QtGui.QLineEdit(AddAddressDialog) - self.newAddressLabel.setObjectName(_fromUtf8("newAddressLabel")) - self.formLayout.setWidget(2, QtGui.QFormLayout.SpanningRole, self.newAddressLabel) - self.label = QtGui.QLabel(AddAddressDialog) - self.label.setObjectName(_fromUtf8("label")) - self.formLayout.setWidget(4, QtGui.QFormLayout.LabelRole, self.label) - self.lineEditAddress = QtGui.QLineEdit(AddAddressDialog) - self.lineEditAddress.setObjectName(_fromUtf8("lineEditAddress")) - self.formLayout.setWidget(5, QtGui.QFormLayout.SpanningRole, self.lineEditAddress) - self.labelAddressCheck = QtGui.QLabel(AddAddressDialog) - self.labelAddressCheck.setText(_fromUtf8("")) - self.labelAddressCheck.setWordWrap(True) - self.labelAddressCheck.setObjectName(_fromUtf8("labelAddressCheck")) - self.formLayout.setWidget(6, QtGui.QFormLayout.SpanningRole, self.labelAddressCheck) - self.buttonBox = QtGui.QDialogButtonBox(AddAddressDialog) - self.buttonBox.setOrientation(QtCore.Qt.Horizontal) - self.buttonBox.setStandardButtons(QtGui.QDialogButtonBox.Cancel|QtGui.QDialogButtonBox.Ok) - self.buttonBox.setObjectName(_fromUtf8("buttonBox")) - self.formLayout.setWidget(7, QtGui.QFormLayout.FieldRole, self.buttonBox) - - self.retranslateUi(AddAddressDialog) - QtCore.QObject.connect(self.buttonBox, QtCore.SIGNAL(_fromUtf8("accepted()")), AddAddressDialog.accept) - QtCore.QObject.connect(self.buttonBox, QtCore.SIGNAL(_fromUtf8("rejected()")), AddAddressDialog.reject) - QtCore.QMetaObject.connectSlotsByName(AddAddressDialog) - - def retranslateUi(self, AddAddressDialog): - AddAddressDialog.setWindowTitle(_translate("AddAddressDialog", "Add new entry", None)) - self.label_2.setText(_translate("AddAddressDialog", "Label", None)) - self.label.setText(_translate("AddAddressDialog", "Address", None)) - diff --git a/src/bitmessageqt/addaddressdialog.ui b/src/bitmessageqt/addaddressdialog.ui index d7963e0a..09701fa4 100644 --- a/src/bitmessageqt/addaddressdialog.ui +++ b/src/bitmessageqt/addaddressdialog.ui @@ -7,9 +7,15 @@ 0 0 368 - 162 + 232 + + + 368 + 200 + + Add new entry @@ -25,7 +31,7 @@ - + @@ -47,7 +53,7 @@ - + Qt::Horizontal @@ -57,6 +63,19 @@ + + + + Qt::Vertical + + + + 20 + 40 + + + + diff --git a/src/bitmessageqt/dialogs.py b/src/bitmessageqt/dialogs.py index d8133eb1..5eeaaadd 100644 --- a/src/bitmessageqt/dialogs.py +++ b/src/bitmessageqt/dialogs.py @@ -1,42 +1,107 @@ from PyQt4 import QtCore, QtGui -from addaddressdialog import Ui_AddAddressDialog -from addresses import decodeAddress +from addresses import decodeAddress, encodeVarint from tr import _translate +from retranslateui import RetranslateMixin +import widgets + +import hashlib +from inventory import Inventory -class AddAddressDialog(QtGui.QDialog): +class AddressCheckMixin(object): - def __init__(self, parent): - QtGui.QWidget.__init__(self, parent) - self.ui = Ui_AddAddressDialog() - self.ui.setupUi(self) - self.parent = parent - QtCore.QObject.connect(self.ui.lineEditAddress, QtCore.SIGNAL( + def __init__(self): + self.valid = False + QtCore.QObject.connect(self.lineEditAddress, QtCore.SIGNAL( "textChanged(QString)"), self.addressChanged) + def _onSuccess(self, addressVersion, streamNumber, ripe): + pass + def addressChanged(self, QString): - status, a, b, c = decodeAddress(str(QString)) - if status == 'missingbm': - self.ui.labelAddressCheck.setText(_translate( + status, addressVersion, streamNumber, ripe = decodeAddress( + str(QString)) + self.valid = status == 'success' + if self.valid: + self.labelAddressCheck.setText( + _translate("MainWindow", "Address is valid.")) + self._onSuccess(addressVersion, streamNumber, ripe) + elif status == 'missingbm': + self.labelAddressCheck.setText(_translate( "MainWindow", "The address should start with ''BM-''")) elif status == 'checksumfailed': - self.ui.labelAddressCheck.setText(_translate( - "MainWindow", "The address is not typed or copied correctly (the checksum failed).")) + self.labelAddressCheck.setText(_translate( + "MainWindow", + "The address is not typed or copied correctly" + " (the checksum failed)." + )) elif status == 'versiontoohigh': - self.ui.labelAddressCheck.setText(_translate( - "MainWindow", "The version number of this address is higher than this software can support. Please upgrade Bitmessage.")) + self.labelAddressCheck.setText(_translate( + "MainWindow", + "The version number of this address is higher than this" + " software can support. Please upgrade Bitmessage." + )) elif status == 'invalidcharacters': - self.ui.labelAddressCheck.setText(_translate( + self.labelAddressCheck.setText(_translate( "MainWindow", "The address contains invalid characters.")) elif status == 'ripetooshort': - self.ui.labelAddressCheck.setText(_translate( - "MainWindow", "Some data encoded in the address is too short.")) + self.labelAddressCheck.setText(_translate( + "MainWindow", + "Some data encoded in the address is too short." + )) elif status == 'ripetoolong': - self.ui.labelAddressCheck.setText(_translate( + self.labelAddressCheck.setText(_translate( "MainWindow", "Some data encoded in the address is too long.")) elif status == 'varintmalformed': - self.ui.labelAddressCheck.setText(_translate( - "MainWindow", "Some data encoded in the address is malformed.")) - elif status == 'success': - self.ui.labelAddressCheck.setText( - _translate("MainWindow", "Address is valid.")) + self.labelAddressCheck.setText(_translate( + "MainWindow", + "Some data encoded in the address is malformed." + )) + + +class AddAddressDialog(QtGui.QDialog, RetranslateMixin, AddressCheckMixin): + + def __init__(self, parent=None): + super(AddAddressDialog, self).__init__(parent) + widgets.load('addaddressdialog.ui', self) + AddressCheckMixin.__init__(self) + + +class NewSubscriptionDialog( + QtGui.QDialog, RetranslateMixin, AddressCheckMixin): + + def __init__(self, parent=None): + super(NewSubscriptionDialog, self).__init__(parent) + widgets.load('newsubscriptiondialog.ui', self) + AddressCheckMixin.__init__(self) + + def _onSuccess(self, addressVersion, streamNumber, ripe): + if addressVersion <= 3: + self.checkBoxDisplayMessagesAlreadyInInventory.setText(_translate( + "MainWindow", + "Address is an old type. We cannot display its past" + " broadcasts." + )) + else: + Inventory().flush() + doubleHashOfAddressData = hashlib.sha512(hashlib.sha512( + encodeVarint(addressVersion) + + encodeVarint(streamNumber) + ripe + ).digest()).digest() + tag = doubleHashOfAddressData[32:] + self.recent = Inventory().by_type_and_tag(3, tag) + count = len(self.recent) + if count == 0: + self.checkBoxDisplayMessagesAlreadyInInventory.setText( + _translate( + "MainWindow", + "There are no recent broadcasts from this address" + " to display." + )) + else: + self.checkBoxDisplayMessagesAlreadyInInventory.setEnabled(True) + self.checkBoxDisplayMessagesAlreadyInInventory.setText( + _translate( + "MainWindow", + "Display the %1 recent broadcast(s) from this address." + ).arg(count)) diff --git a/src/bitmessageqt/newsubscriptiondialog.py b/src/bitmessageqt/newsubscriptiondialog.py deleted file mode 100644 index a63cce4a..00000000 --- a/src/bitmessageqt/newsubscriptiondialog.py +++ /dev/null @@ -1,69 +0,0 @@ -# -*- coding: utf-8 -*- - -# Form implementation generated from reading ui file 'newsubscriptiondialog.ui' -# -# Created: Sat Nov 30 21:53:38 2013 -# by: PyQt4 UI code generator 4.10.3 -# -# WARNING! All changes made in this file will be lost! - -from PyQt4 import QtCore, QtGui - -try: - _fromUtf8 = QtCore.QString.fromUtf8 -except AttributeError: - def _fromUtf8(s): - return s - -try: - _encoding = QtGui.QApplication.UnicodeUTF8 - def _translate(context, text, disambig): - return QtGui.QApplication.translate(context, text, disambig, _encoding) -except AttributeError: - def _translate(context, text, disambig): - return QtGui.QApplication.translate(context, text, disambig) - -class Ui_NewSubscriptionDialog(object): - def setupUi(self, NewSubscriptionDialog): - NewSubscriptionDialog.setObjectName(_fromUtf8("NewSubscriptionDialog")) - NewSubscriptionDialog.resize(368, 173) - self.formLayout = QtGui.QFormLayout(NewSubscriptionDialog) - self.formLayout.setObjectName(_fromUtf8("formLayout")) - self.label_2 = QtGui.QLabel(NewSubscriptionDialog) - self.label_2.setObjectName(_fromUtf8("label_2")) - self.formLayout.setWidget(0, QtGui.QFormLayout.LabelRole, self.label_2) - self.newsubscriptionlabel = QtGui.QLineEdit(NewSubscriptionDialog) - self.newsubscriptionlabel.setObjectName(_fromUtf8("newsubscriptionlabel")) - self.formLayout.setWidget(1, QtGui.QFormLayout.SpanningRole, self.newsubscriptionlabel) - self.label = QtGui.QLabel(NewSubscriptionDialog) - self.label.setObjectName(_fromUtf8("label")) - self.formLayout.setWidget(2, QtGui.QFormLayout.LabelRole, self.label) - self.lineEditSubscriptionAddress = QtGui.QLineEdit(NewSubscriptionDialog) - self.lineEditSubscriptionAddress.setObjectName(_fromUtf8("lineEditSubscriptionAddress")) - self.formLayout.setWidget(3, QtGui.QFormLayout.SpanningRole, self.lineEditSubscriptionAddress) - self.labelAddressCheck = QtGui.QLabel(NewSubscriptionDialog) - self.labelAddressCheck.setText(_fromUtf8("")) - self.labelAddressCheck.setWordWrap(True) - self.labelAddressCheck.setObjectName(_fromUtf8("labelAddressCheck")) - self.formLayout.setWidget(4, QtGui.QFormLayout.SpanningRole, self.labelAddressCheck) - self.checkBoxDisplayMessagesAlreadyInInventory = QtGui.QCheckBox(NewSubscriptionDialog) - self.checkBoxDisplayMessagesAlreadyInInventory.setEnabled(False) - self.checkBoxDisplayMessagesAlreadyInInventory.setObjectName(_fromUtf8("checkBoxDisplayMessagesAlreadyInInventory")) - self.formLayout.setWidget(5, QtGui.QFormLayout.SpanningRole, self.checkBoxDisplayMessagesAlreadyInInventory) - self.buttonBox = QtGui.QDialogButtonBox(NewSubscriptionDialog) - self.buttonBox.setOrientation(QtCore.Qt.Horizontal) - self.buttonBox.setStandardButtons(QtGui.QDialogButtonBox.Cancel|QtGui.QDialogButtonBox.Ok) - self.buttonBox.setObjectName(_fromUtf8("buttonBox")) - self.formLayout.setWidget(6, QtGui.QFormLayout.FieldRole, self.buttonBox) - - self.retranslateUi(NewSubscriptionDialog) - QtCore.QObject.connect(self.buttonBox, QtCore.SIGNAL(_fromUtf8("accepted()")), NewSubscriptionDialog.accept) - QtCore.QObject.connect(self.buttonBox, QtCore.SIGNAL(_fromUtf8("rejected()")), NewSubscriptionDialog.reject) - QtCore.QMetaObject.connectSlotsByName(NewSubscriptionDialog) - - def retranslateUi(self, NewSubscriptionDialog): - NewSubscriptionDialog.setWindowTitle(_translate("NewSubscriptionDialog", "Add new entry", None)) - self.label_2.setText(_translate("NewSubscriptionDialog", "Label", None)) - self.label.setText(_translate("NewSubscriptionDialog", "Address", None)) - self.checkBoxDisplayMessagesAlreadyInInventory.setText(_translate("NewSubscriptionDialog", "Enter an address above.", None)) - diff --git a/src/bitmessageqt/newsubscriptiondialog.ui b/src/bitmessageqt/newsubscriptiondialog.ui index ed8615f4..ec67efa3 100644 --- a/src/bitmessageqt/newsubscriptiondialog.ui +++ b/src/bitmessageqt/newsubscriptiondialog.ui @@ -7,9 +7,15 @@ 0 0 368 - 173 + 254 + + + 368 + 200 + + Add new entry @@ -22,7 +28,7 @@ - + @@ -32,7 +38,7 @@ - + @@ -44,17 +50,17 @@ - + false - CheckBox + Enter an address above. - + Qt::Horizontal @@ -64,6 +70,19 @@ + + + + Qt::Vertical + + + + 20 + 40 + + + + From e2e7e16ab703477a42e25e4d411572fdb2d64035 Mon Sep 17 00:00:00 2001 From: Dmitri Bogomolov <4glitch@gmail.com> Date: Fri, 13 Oct 2017 01:36:54 +0300 Subject: [PATCH 334/407] Moved aboutDialog and iconGlossaryDialog also into dialogs module --- src/bitmessageqt/__init__.py | 37 +----- src/bitmessageqt/about.py | 74 ------------ src/bitmessageqt/about.ui | 187 ++++++++++++++----------------- src/bitmessageqt/dialogs.py | 26 +++++ src/bitmessageqt/iconglossary.py | 98 ---------------- src/bitmessageqt/iconglossary.ui | 2 +- 6 files changed, 113 insertions(+), 311 deletions(-) delete mode 100644 src/bitmessageqt/about.py delete mode 100644 src/bitmessageqt/iconglossary.py diff --git a/src/bitmessageqt/__init__.py b/src/bitmessageqt/__init__.py index 10cbc8f9..a4ce38ac 100644 --- a/src/bitmessageqt/__init__.py +++ b/src/bitmessageqt/__init__.py @@ -35,9 +35,7 @@ from emailgateway import * from settings import * import settingsmixin import support -from about import * from help import * -from iconglossary import * from connect import * import locale import sys @@ -62,6 +60,7 @@ from helper_generic import powQueueSize from inventory import ( Inventory, PendingDownloadQueue, PendingUpload, PendingUploadDeadlineException) +from uisignaler import UISignaler import knownnodes import paths from proofofwork import getPowType @@ -70,9 +69,9 @@ import shutdown import state from statusbar import BMStatusBar from network.asyncore_pollchoose import set_rates -from version import softwareVersion import sound + try: from plugins.plugin import get_plugin, get_plugins except ImportError: @@ -2220,10 +2219,7 @@ class MyForm(settingsmixin.SMainWindow): )) def click_pushButtonStatusIcon(self): - logger.debug('click_pushButtonStatusIcon') - self.iconGlossaryInstance = iconGlossaryDialog(self) - if self.iconGlossaryInstance.exec_(): - pass + dialogs.IconGlossaryDialog(self, config=BMConfigParser()).exec_() def click_actionHelp(self): self.helpDialogInstance = helpDialog(self) @@ -2233,8 +2229,7 @@ class MyForm(settingsmixin.SMainWindow): support.createSupportMessage(self) def click_actionAbout(self): - self.aboutDialogInstance = aboutDialog(self) - self.aboutDialogInstance.exec_() + dialogs.AboutDialog(self).exec_() def click_actionSettings(self): self.settingsDialogInstance = settingsDialog(self) @@ -3975,16 +3970,6 @@ class connectDialog(QtGui.QDialog): self.parent = parent QtGui.QWidget.resize(self, QtGui.QWidget.sizeHint(self)) -class aboutDialog(QtGui.QDialog): - - def __init__(self, parent): - QtGui.QWidget.__init__(self, parent) - self.ui = Ui_aboutDialog() - self.ui.setupUi(self) - self.parent = parent - self.ui.label.setText("PyBitmessage " + softwareVersion) - self.ui.labelVersion.setText(paths.lastCommit()) - class regenerateAddressesDialog(QtGui.QDialog): @@ -4312,18 +4297,6 @@ class NewAddressDialog(QtGui.QDialog): QtGui.QWidget.resize(self, QtGui.QWidget.sizeHint(self)) -class iconGlossaryDialog(QtGui.QDialog): - - def __init__(self, parent): - QtGui.QWidget.__init__(self, parent) - self.ui = Ui_iconGlossaryDialog() - self.ui.setupUi(self) - self.parent = parent - self.ui.labelPortNumber.setText(_translate( - "MainWindow", "You are using TCP port %1. (This can be changed in the settings).").arg(str(BMConfigParser().getint('bitmessagesettings', 'port')))) - QtGui.QWidget.resize(self, QtGui.QWidget.sizeHint(self)) - - # In order for the time columns on the Inbox and Sent tabs to be sorted # correctly (rather than alphabetically), we need to overload the < # operator and use this class instead of QTableWidgetItem. @@ -4332,8 +4305,6 @@ class myTableWidgetItem(QTableWidgetItem): def __lt__(self, other): return int(self.data(33).toPyObject()) < int(other.data(33).toPyObject()) -from uisignaler import UISignaler - app = None myapp = None diff --git a/src/bitmessageqt/about.py b/src/bitmessageqt/about.py deleted file mode 100644 index a3483675..00000000 --- a/src/bitmessageqt/about.py +++ /dev/null @@ -1,74 +0,0 @@ -# -*- coding: utf-8 -*- - -# Form implementation generated from reading ui file 'about.ui' -# -# Created: Tue Jan 21 22:29:38 2014 -# by: PyQt4 UI code generator 4.10.3 -# -# WARNING! All changes made in this file will be lost! - -from PyQt4 import QtCore, QtGui - -try: - _fromUtf8 = QtCore.QString.fromUtf8 -except AttributeError: - def _fromUtf8(s): - return s - -try: - _encoding = QtGui.QApplication.UnicodeUTF8 - def _translate(context, text, disambig): - return QtGui.QApplication.translate(context, text, disambig, _encoding) -except AttributeError: - def _translate(context, text, disambig): - return QtGui.QApplication.translate(context, text, disambig) - - -class Ui_aboutDialog(object): - def setupUi(self, aboutDialog): - aboutDialog.setObjectName(_fromUtf8("aboutDialog")) - aboutDialog.resize(360, 315) - self.buttonBox = QtGui.QDialogButtonBox(aboutDialog) - self.buttonBox.setGeometry(QtCore.QRect(20, 280, 311, 32)) - self.buttonBox.setOrientation(QtCore.Qt.Horizontal) - self.buttonBox.setStandardButtons(QtGui.QDialogButtonBox.Ok) - self.buttonBox.setObjectName(_fromUtf8("buttonBox")) - self.label = QtGui.QLabel(aboutDialog) - self.label.setGeometry(QtCore.QRect(10, 106, 341, 20)) - font = QtGui.QFont() - font.setBold(True) - font.setWeight(75) - self.label.setFont(font) - self.label.setAlignment(QtCore.Qt.AlignCenter|QtCore.Qt.AlignVCenter) - self.label.setObjectName(_fromUtf8("label")) - self.labelVersion = QtGui.QLabel(aboutDialog) - self.labelVersion.setGeometry(QtCore.QRect(10, 116, 341, 41)) - self.labelVersion.setObjectName(_fromUtf8("labelVersion")) - self.labelVersion.setAlignment(QtCore.Qt.AlignCenter|QtCore.Qt.AlignVCenter) - self.label_2 = QtGui.QLabel(aboutDialog) - self.label_2.setGeometry(QtCore.QRect(10, 150, 341, 41)) - self.label_2.setAlignment(QtCore.Qt.AlignCenter) - self.label_2.setObjectName(_fromUtf8("label_2")) - self.label_3 = QtGui.QLabel(aboutDialog) - self.label_3.setGeometry(QtCore.QRect(20, 200, 331, 71)) - self.label_3.setWordWrap(True) - self.label_3.setOpenExternalLinks(True) - self.label_3.setObjectName(_fromUtf8("label_3")) - self.label_5 = QtGui.QLabel(aboutDialog) - self.label_5.setGeometry(QtCore.QRect(10, 190, 341, 20)) - self.label_5.setAlignment(QtCore.Qt.AlignCenter) - self.label_5.setObjectName(_fromUtf8("label_5")) - - self.retranslateUi(aboutDialog) - QtCore.QObject.connect(self.buttonBox, QtCore.SIGNAL(_fromUtf8("accepted()")), aboutDialog.accept) - QtCore.QObject.connect(self.buttonBox, QtCore.SIGNAL(_fromUtf8("rejected()")), aboutDialog.reject) - QtCore.QMetaObject.connectSlotsByName(aboutDialog) - - def retranslateUi(self, aboutDialog): - aboutDialog.setWindowTitle(_translate("aboutDialog", "About", None)) - self.label.setText(_translate("aboutDialog", "PyBitmessage", None)) - self.labelVersion.setText(_translate("aboutDialog", "version ?", None)) - self.label_2.setText(_translate("aboutDialog", "

Copyright © 2012-2016 Jonathan Warren
Copyright © 2013-2016 The Bitmessage Developers

", None)) - self.label_3.setText(_translate("aboutDialog", "

Distributed under the MIT/X11 software license; see http://www.opensource.org/licenses/mit-license.php

", None)) - self.label_5.setText(_translate("aboutDialog", "This is Beta software.", None)) - diff --git a/src/bitmessageqt/about.ui b/src/bitmessageqt/about.ui index 3deab41b..d09cbc4d 100644 --- a/src/bitmessageqt/about.ui +++ b/src/bitmessageqt/about.ui @@ -6,117 +6,94 @@ 0 0 - 360 - 315 + 430 + 340 About - - - - 20 - 280 - 311 - 32 - - - - Qt::Horizontal - - - QDialogButtonBox::Ok - - - - - - 70 - 126 - 111 - 20 - - - - - 75 - true - - - - PyBitmessage - - - Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - - - - - - 190 - 126 - 161 - 20 - - - - version ? - - - - - - 10 - 150 - 341 - 41 - - - - <html><head/><body><p>Copyright © 2012-2014 Jonathan Warren<br/>Copyright © 2013-2014 The Bitmessage Developers</p></body></html> - - - Qt::AlignCenter - - - - - - 20 - 200 - 331 - 71 - - - - <html><head/><body><p>Distributed under the MIT/X11 software license; see <a href="http://www.opensource.org/licenses/mit-license.php"><span style=" text-decoration: underline; color:#0000ff;">http://www.opensource.org/licenses/mit-license.php</span></a></p></body></html> - - - true - - - true - - - - - - 10 - 190 - 341 - 20 - - - - This is Beta software. - - - Qt::AlignCenter - - + + + + + + + + :/newPrefix/images/can-icon-24px.png + + + true + + + Qt::AlignCenter + + + + + + + + 75 + true + + + + PyBitmessage + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + <html><head/><body><p>Copyright © 2012-2016 Jonathan Warren<br/>Copyright © 2013-2017 The Bitmessage Developers</p></body></html> + + + Qt::AlignLeft + + + + + + + This is Beta software. + + + Qt::AlignCenter + + + + + + + <html><head/><body><p>Distributed under the MIT/X11 software license; see <a href="http://www.opensource.org/licenses/mit-license.php"><span style=" text-decoration: underline; color:#0000ff;">http://www.opensource.org/licenses/mit-license.php</span></a></p></body></html> + + + true + + + true + + + + + + + Qt::Horizontal + + + QDialogButtonBox::Ok + + + + - + + + buttonBox diff --git a/src/bitmessageqt/dialogs.py b/src/bitmessageqt/dialogs.py index 5eeaaadd..f9615612 100644 --- a/src/bitmessageqt/dialogs.py +++ b/src/bitmessageqt/dialogs.py @@ -5,7 +5,9 @@ from retranslateui import RetranslateMixin import widgets import hashlib +import paths from inventory import Inventory +from version import softwareVersion class AddressCheckMixin(object): @@ -105,3 +107,27 @@ class NewSubscriptionDialog( "MainWindow", "Display the %1 recent broadcast(s) from this address." ).arg(count)) + + +class AboutDialog(QtGui.QDialog, RetranslateMixin): + def __init__(self, parent=None): + super(AboutDialog, self).__init__(parent) + widgets.load('about.ui', self) + commit = paths.lastCommit()[:7] + label = "PyBitmessage " + softwareVersion + if commit: + label += '-' + commit + self.labelVersion.setText(label) + QtGui.QWidget.resize(self, QtGui.QWidget.sizeHint(self)) + + +class IconGlossaryDialog(QtGui.QDialog, RetranslateMixin): + def __init__(self, parent=None, config=None): + super(IconGlossaryDialog, self).__init__(parent) + widgets.load('iconglossary.ui', self) + + self.labelPortNumber.setText(_translate( + "iconGlossaryDialog", + "You are using TCP port %1. (This can be changed in the settings)." + ).arg(config.getint('bitmessagesettings', 'port'))) + QtGui.QWidget.resize(self, QtGui.QWidget.sizeHint(self)) diff --git a/src/bitmessageqt/iconglossary.py b/src/bitmessageqt/iconglossary.py deleted file mode 100644 index 32d92db6..00000000 --- a/src/bitmessageqt/iconglossary.py +++ /dev/null @@ -1,98 +0,0 @@ -# -*- coding: utf-8 -*- - -# Form implementation generated from reading ui file 'iconglossary.ui' -# -# Created: Thu Jun 13 20:15:48 2013 -# by: PyQt4 UI code generator 4.10.1 -# -# WARNING! All changes made in this file will be lost! - -from PyQt4 import QtCore, QtGui - -try: - _fromUtf8 = QtCore.QString.fromUtf8 -except AttributeError: - def _fromUtf8(s): - return s - -try: - _encoding = QtGui.QApplication.UnicodeUTF8 - def _translate(context, text, disambig): - return QtGui.QApplication.translate(context, text, disambig, _encoding) -except AttributeError: - def _translate(context, text, disambig): - return QtGui.QApplication.translate(context, text, disambig) - -class Ui_iconGlossaryDialog(object): - def setupUi(self, iconGlossaryDialog): - iconGlossaryDialog.setObjectName(_fromUtf8("iconGlossaryDialog")) - iconGlossaryDialog.resize(424, 282) - self.gridLayout = QtGui.QGridLayout(iconGlossaryDialog) - self.gridLayout.setObjectName(_fromUtf8("gridLayout")) - self.groupBox = QtGui.QGroupBox(iconGlossaryDialog) - self.groupBox.setObjectName(_fromUtf8("groupBox")) - self.gridLayout_2 = QtGui.QGridLayout(self.groupBox) - self.gridLayout_2.setObjectName(_fromUtf8("gridLayout_2")) - self.label = QtGui.QLabel(self.groupBox) - self.label.setText(_fromUtf8("")) - self.label.setPixmap(QtGui.QPixmap(_fromUtf8(":/newPrefix/images/redicon.png"))) - self.label.setObjectName(_fromUtf8("label")) - self.gridLayout_2.addWidget(self.label, 0, 0, 1, 1) - self.label_2 = QtGui.QLabel(self.groupBox) - self.label_2.setObjectName(_fromUtf8("label_2")) - self.gridLayout_2.addWidget(self.label_2, 0, 1, 1, 1) - self.label_3 = QtGui.QLabel(self.groupBox) - self.label_3.setText(_fromUtf8("")) - self.label_3.setPixmap(QtGui.QPixmap(_fromUtf8(":/newPrefix/images/yellowicon.png"))) - self.label_3.setObjectName(_fromUtf8("label_3")) - self.gridLayout_2.addWidget(self.label_3, 1, 0, 1, 1) - self.label_4 = QtGui.QLabel(self.groupBox) - self.label_4.setAlignment(QtCore.Qt.AlignLeading|QtCore.Qt.AlignLeft|QtCore.Qt.AlignTop) - self.label_4.setWordWrap(True) - self.label_4.setObjectName(_fromUtf8("label_4")) - self.gridLayout_2.addWidget(self.label_4, 1, 1, 2, 1) - spacerItem = QtGui.QSpacerItem(20, 73, QtGui.QSizePolicy.Minimum, QtGui.QSizePolicy.Expanding) - self.gridLayout_2.addItem(spacerItem, 2, 0, 2, 1) - self.labelPortNumber = QtGui.QLabel(self.groupBox) - self.labelPortNumber.setObjectName(_fromUtf8("labelPortNumber")) - self.gridLayout_2.addWidget(self.labelPortNumber, 3, 1, 1, 1) - self.label_5 = QtGui.QLabel(self.groupBox) - self.label_5.setText(_fromUtf8("")) - self.label_5.setPixmap(QtGui.QPixmap(_fromUtf8(":/newPrefix/images/greenicon.png"))) - self.label_5.setObjectName(_fromUtf8("label_5")) - self.gridLayout_2.addWidget(self.label_5, 4, 0, 1, 1) - self.label_6 = QtGui.QLabel(self.groupBox) - self.label_6.setWordWrap(True) - self.label_6.setObjectName(_fromUtf8("label_6")) - self.gridLayout_2.addWidget(self.label_6, 4, 1, 1, 1) - self.gridLayout.addWidget(self.groupBox, 0, 0, 1, 1) - self.buttonBox = QtGui.QDialogButtonBox(iconGlossaryDialog) - self.buttonBox.setOrientation(QtCore.Qt.Horizontal) - self.buttonBox.setStandardButtons(QtGui.QDialogButtonBox.Ok) - self.buttonBox.setObjectName(_fromUtf8("buttonBox")) - self.gridLayout.addWidget(self.buttonBox, 1, 0, 1, 1) - - self.retranslateUi(iconGlossaryDialog) - QtCore.QObject.connect(self.buttonBox, QtCore.SIGNAL(_fromUtf8("accepted()")), iconGlossaryDialog.accept) - QtCore.QObject.connect(self.buttonBox, QtCore.SIGNAL(_fromUtf8("rejected()")), iconGlossaryDialog.reject) - QtCore.QMetaObject.connectSlotsByName(iconGlossaryDialog) - - def retranslateUi(self, iconGlossaryDialog): - iconGlossaryDialog.setWindowTitle(_translate("iconGlossaryDialog", "Icon Glossary", None)) - self.groupBox.setTitle(_translate("iconGlossaryDialog", "Icon Glossary", None)) - self.label_2.setText(_translate("iconGlossaryDialog", "You have no connections with other peers. ", None)) - self.label_4.setText(_translate("iconGlossaryDialog", "You have made at least one connection to a peer using an outgoing connection but you have not yet received any incoming connections. Your firewall or home router probably isn\'t configured to forward incoming TCP connections to your computer. Bitmessage will work just fine but it would help the Bitmessage network if you allowed for incoming connections and will help you be a better-connected node.", None)) - self.labelPortNumber.setText(_translate("iconGlossaryDialog", "You are using TCP port ?. (This can be changed in the settings).", None)) - self.label_6.setText(_translate("iconGlossaryDialog", "You do have connections with other peers and your firewall is correctly configured.", None)) - -import bitmessage_icons_rc - -if __name__ == "__main__": - import sys - app = QtGui.QApplication(sys.argv) - iconGlossaryDialog = QtGui.QDialog() - ui = Ui_iconGlossaryDialog() - ui.setupUi(iconGlossaryDialog) - iconGlossaryDialog.show() - sys.exit(app.exec_()) - diff --git a/src/bitmessageqt/iconglossary.ui b/src/bitmessageqt/iconglossary.ui index 870a90ee..1bac94c8 100644 --- a/src/bitmessageqt/iconglossary.ui +++ b/src/bitmessageqt/iconglossary.ui @@ -76,7 +76,7 @@ - You are using TCP port ?. (This can be changed in the settings). + You are using TCP port ?. (This can be changed in the settings). From c965a1d2aaad6b387868d5f7e0cd21c5e60df731 Mon Sep 17 00:00:00 2001 From: Dmitri Bogomolov <4glitch@gmail.com> Date: Mon, 16 Oct 2017 16:09:52 +0300 Subject: [PATCH 335/407] Moved helpDialog and connectDialog also into dialogs module --- src/bitmessageqt/__init__.py | 35 ++++---------------- src/bitmessageqt/connect.py | 64 ------------------------------------ src/bitmessageqt/connect.ui | 11 +++++-- src/bitmessageqt/dialogs.py | 14 ++++++++ src/bitmessageqt/help.py | 48 --------------------------- 5 files changed, 29 insertions(+), 143 deletions(-) delete mode 100644 src/bitmessageqt/connect.py delete mode 100644 src/bitmessageqt/help.py diff --git a/src/bitmessageqt/__init__.py b/src/bitmessageqt/__init__.py index a4ce38ac..ac753f5f 100644 --- a/src/bitmessageqt/__init__.py +++ b/src/bitmessageqt/__init__.py @@ -35,8 +35,6 @@ from emailgateway import * from settings import * import settingsmixin import support -from help import * -from connect import * import locale import sys import time @@ -1465,13 +1463,13 @@ class MyForm(settingsmixin.SMainWindow): NewChanDialog(self) def showConnectDialog(self): - self.connectDialogInstance = connectDialog(self) - if self.connectDialogInstance.exec_(): - if self.connectDialogInstance.ui.radioButtonConnectNow.isChecked(): + dialog = dialogs.ConnectDialog(self) + if dialog.exec_(): + if dialog.radioButtonConnectNow.isChecked(): BMConfigParser().remove_option( 'bitmessagesettings', 'dontconnect') BMConfigParser().save() - elif self.connectDialogInstance.ui.radioButtonConfigureNetwork.isChecked(): + elif dialog.radioButtonConfigureNetwork.isChecked(): self.click_actionSettings() def showMigrationWizard(self, level): @@ -1480,7 +1478,7 @@ class MyForm(settingsmixin.SMainWindow): pass else: pass - + def changeEvent(self, event): if event.type() == QtCore.QEvent.LanguageChange: self.ui.retranslateUi(self) @@ -2222,8 +2220,7 @@ class MyForm(settingsmixin.SMainWindow): dialogs.IconGlossaryDialog(self, config=BMConfigParser()).exec_() def click_actionHelp(self): - self.helpDialogInstance = helpDialog(self) - self.helpDialogInstance.exec_() + dialogs.HelpDialog(self).exec_() def click_actionSupport(self): support.createSupportMessage(self) @@ -3949,26 +3946,6 @@ class MyForm(settingsmixin.SMainWindow): loadMethod = getattr(obj, "loadSettings", None) if callable (loadMethod): obj.loadSettings() - - -class helpDialog(QtGui.QDialog): - - def __init__(self, parent): - QtGui.QWidget.__init__(self, parent) - self.ui = Ui_helpDialog() - self.ui.setupUi(self) - self.parent = parent - self.ui.labelHelpURI.setOpenExternalLinks(True) - QtGui.QWidget.resize(self, QtGui.QWidget.sizeHint(self)) - -class connectDialog(QtGui.QDialog): - - def __init__(self, parent): - QtGui.QWidget.__init__(self, parent) - self.ui = Ui_connectDialog() - self.ui.setupUi(self) - self.parent = parent - QtGui.QWidget.resize(self, QtGui.QWidget.sizeHint(self)) class regenerateAddressesDialog(QtGui.QDialog): diff --git a/src/bitmessageqt/connect.py b/src/bitmessageqt/connect.py deleted file mode 100644 index 9151156f..00000000 --- a/src/bitmessageqt/connect.py +++ /dev/null @@ -1,64 +0,0 @@ -# -*- coding: utf-8 -*- - -# Form implementation generated from reading ui file 'connect.ui' -# -# Created: Wed Jul 24 12:42:01 2013 -# by: PyQt4 UI code generator 4.10 -# -# WARNING! All changes made in this file will be lost! - -from PyQt4 import QtCore, QtGui - -try: - _fromUtf8 = QtCore.QString.fromUtf8 -except AttributeError: - def _fromUtf8(s): - return s - -try: - _encoding = QtGui.QApplication.UnicodeUTF8 - def _translate(context, text, disambig): - return QtGui.QApplication.translate(context, text, disambig, _encoding) -except AttributeError: - def _translate(context, text, disambig): - return QtGui.QApplication.translate(context, text, disambig) - -class Ui_connectDialog(object): - def setupUi(self, connectDialog): - connectDialog.setObjectName(_fromUtf8("connectDialog")) - connectDialog.resize(400, 124) - self.gridLayout = QtGui.QGridLayout(connectDialog) - self.gridLayout.setObjectName(_fromUtf8("gridLayout")) - self.label = QtGui.QLabel(connectDialog) - self.label.setObjectName(_fromUtf8("label")) - self.gridLayout.addWidget(self.label, 0, 0, 1, 2) - self.radioButtonConnectNow = QtGui.QRadioButton(connectDialog) - self.radioButtonConnectNow.setChecked(True) - self.radioButtonConnectNow.setObjectName(_fromUtf8("radioButtonConnectNow")) - self.gridLayout.addWidget(self.radioButtonConnectNow, 1, 0, 1, 2) - self.radioButtonConfigureNetwork = QtGui.QRadioButton(connectDialog) - self.radioButtonConfigureNetwork.setObjectName(_fromUtf8("radioButtonConfigureNetwork")) - self.gridLayout.addWidget(self.radioButtonConfigureNetwork, 2, 0, 1, 2) - self.radioButtonWorkOffline = QtGui.QRadioButton(connectDialog) - self.radioButtonWorkOffline.setObjectName(_fromUtf8("radioButtonWorkOffline")) - self.gridLayout.addWidget(self.radioButtonWorkOffline, 3, 0, 1, 2) - spacerItem = QtGui.QSpacerItem(185, 24, QtGui.QSizePolicy.Expanding, QtGui.QSizePolicy.Minimum) - self.gridLayout.addItem(spacerItem, 4, 0, 1, 1) - self.buttonBox = QtGui.QDialogButtonBox(connectDialog) - self.buttonBox.setOrientation(QtCore.Qt.Horizontal) - self.buttonBox.setStandardButtons(QtGui.QDialogButtonBox.Ok) - self.buttonBox.setObjectName(_fromUtf8("buttonBox")) - self.gridLayout.addWidget(self.buttonBox, 4, 1, 1, 1) - - self.retranslateUi(connectDialog) - QtCore.QObject.connect(self.buttonBox, QtCore.SIGNAL(_fromUtf8("accepted()")), connectDialog.accept) - QtCore.QObject.connect(self.buttonBox, QtCore.SIGNAL(_fromUtf8("rejected()")), connectDialog.reject) - QtCore.QMetaObject.connectSlotsByName(connectDialog) - - def retranslateUi(self, connectDialog): - connectDialog.setWindowTitle(_translate("connectDialog", "Bitmessage", None)) - self.label.setText(_translate("connectDialog", "Bitmessage won\'t connect to anyone until you let it. ", None)) - self.radioButtonConnectNow.setText(_translate("connectDialog", "Connect now", None)) - self.radioButtonConfigureNetwork.setText(_translate("connectDialog", "Let me configure special network settings first", None)) - self.radioButtonWorkOffline.setText(_translate("connectDialog", "Work offline", None)) - diff --git a/src/bitmessageqt/connect.ui b/src/bitmessageqt/connect.ui index 74173860..8b76f5ac 100644 --- a/src/bitmessageqt/connect.ui +++ b/src/bitmessageqt/connect.ui @@ -38,7 +38,14 @@ - + + + + Work offline + + + + Qt::Horizontal @@ -51,7 +58,7 @@ - + Qt::Horizontal diff --git a/src/bitmessageqt/dialogs.py b/src/bitmessageqt/dialogs.py index f9615612..3b2cce2d 100644 --- a/src/bitmessageqt/dialogs.py +++ b/src/bitmessageqt/dialogs.py @@ -131,3 +131,17 @@ class IconGlossaryDialog(QtGui.QDialog, RetranslateMixin): "You are using TCP port %1. (This can be changed in the settings)." ).arg(config.getint('bitmessagesettings', 'port'))) QtGui.QWidget.resize(self, QtGui.QWidget.sizeHint(self)) + + +class HelpDialog(QtGui.QDialog, RetranslateMixin): + def __init__(self, parent=None): + super(HelpDialog, self).__init__(parent) + widgets.load('help.ui', self) + QtGui.QWidget.resize(self, QtGui.QWidget.sizeHint(self)) + + +class ConnectDialog(QtGui.QDialog, RetranslateMixin): + def __init__(self, parent=None): + super(ConnectDialog, self).__init__(parent) + widgets.load('connect.ui', self) + QtGui.QWidget.resize(self, QtGui.QWidget.sizeHint(self)) diff --git a/src/bitmessageqt/help.py b/src/bitmessageqt/help.py deleted file mode 100644 index ff876514..00000000 --- a/src/bitmessageqt/help.py +++ /dev/null @@ -1,48 +0,0 @@ -# -*- coding: utf-8 -*- - -# Form implementation generated from reading ui file 'help.ui' -# -# Created: Wed Jan 14 22:42:39 2015 -# by: PyQt4 UI code generator 4.9.4 -# -# WARNING! All changes made in this file will be lost! - -from PyQt4 import QtCore, QtGui - -try: - _fromUtf8 = QtCore.QString.fromUtf8 -except AttributeError: - _fromUtf8 = lambda s: s - -class Ui_helpDialog(object): - def setupUi(self, helpDialog): - helpDialog.setObjectName(_fromUtf8("helpDialog")) - helpDialog.resize(335, 96) - self.formLayout = QtGui.QFormLayout(helpDialog) - self.formLayout.setObjectName(_fromUtf8("formLayout")) - self.labelHelpURI = QtGui.QLabel(helpDialog) - self.labelHelpURI.setOpenExternalLinks(True) - self.labelHelpURI.setObjectName(_fromUtf8("labelHelpURI")) - self.formLayout.setWidget(1, QtGui.QFormLayout.LabelRole, self.labelHelpURI) - self.label = QtGui.QLabel(helpDialog) - self.label.setWordWrap(True) - self.label.setObjectName(_fromUtf8("label")) - self.formLayout.setWidget(0, QtGui.QFormLayout.SpanningRole, self.label) - spacerItem = QtGui.QSpacerItem(40, 20, QtGui.QSizePolicy.Expanding, QtGui.QSizePolicy.Minimum) - self.formLayout.setItem(2, QtGui.QFormLayout.LabelRole, spacerItem) - self.buttonBox = QtGui.QDialogButtonBox(helpDialog) - self.buttonBox.setOrientation(QtCore.Qt.Horizontal) - self.buttonBox.setStandardButtons(QtGui.QDialogButtonBox.Ok) - self.buttonBox.setObjectName(_fromUtf8("buttonBox")) - self.formLayout.setWidget(2, QtGui.QFormLayout.FieldRole, self.buttonBox) - - self.retranslateUi(helpDialog) - QtCore.QObject.connect(self.buttonBox, QtCore.SIGNAL(_fromUtf8("accepted()")), helpDialog.accept) - QtCore.QObject.connect(self.buttonBox, QtCore.SIGNAL(_fromUtf8("rejected()")), helpDialog.reject) - QtCore.QMetaObject.connectSlotsByName(helpDialog) - - def retranslateUi(self, helpDialog): - helpDialog.setWindowTitle(QtGui.QApplication.translate("helpDialog", "Help", None, QtGui.QApplication.UnicodeUTF8)) - self.labelHelpURI.setText(QtGui.QApplication.translate("helpDialog", "https://bitmessage.org/wiki/PyBitmessage_Help", None, QtGui.QApplication.UnicodeUTF8)) - self.label.setText(QtGui.QApplication.translate("helpDialog", "As Bitmessage is a collaborative project, help can be found online in the Bitmessage Wiki:", None, QtGui.QApplication.UnicodeUTF8)) - From 62a930c374c2f416f64764331a51ed507602075a Mon Sep 17 00:00:00 2001 From: Dmitri Bogomolov <4glitch@gmail.com> Date: Tue, 17 Oct 2017 18:00:27 +0300 Subject: [PATCH 336/407] Moved SpecialAddressBehaviorDialog into dialogs module --- src/bitmessageqt/__init__.py | 54 +----------------- src/bitmessageqt/dialogs.py | 65 ++++++++++++++++++++++ src/bitmessageqt/specialaddressbehavior.py | 64 --------------------- 3 files changed, 66 insertions(+), 117 deletions(-) delete mode 100644 src/bitmessageqt/specialaddressbehavior.py diff --git a/src/bitmessageqt/__init__.py b/src/bitmessageqt/__init__.py index ac753f5f..4cb388a4 100644 --- a/src/bitmessageqt/__init__.py +++ b/src/bitmessageqt/__init__.py @@ -30,7 +30,6 @@ from foldertree import * from regenerateaddresses import * from newchandialog import * from safehtmlparser import * -from specialaddressbehavior import * from emailgateway import * from settings import * import settingsmixin @@ -2449,31 +2448,7 @@ class MyForm(settingsmixin.SMainWindow): pass def on_action_SpecialAddressBehaviorDialog(self): - self.dialog = SpecialAddressBehaviorDialog(self) - # For Modal dialogs - if self.dialog.exec_(): - addressAtCurrentRow = self.getCurrentAccount() - if BMConfigParser().safeGetBoolean(addressAtCurrentRow, 'chan'): - return - if self.dialog.ui.radioButtonBehaveNormalAddress.isChecked(): - BMConfigParser().set(str( - addressAtCurrentRow), 'mailinglist', 'false') - # Set the color to either black or grey - if BMConfigParser().getboolean(addressAtCurrentRow, 'enabled'): - self.setCurrentItemColor(QApplication.palette() - .text().color()) - else: - self.setCurrentItemColor(QtGui.QColor(128, 128, 128)) - else: - BMConfigParser().set(str( - addressAtCurrentRow), 'mailinglist', 'true') - BMConfigParser().set(str(addressAtCurrentRow), 'mailinglistname', str( - self.dialog.ui.lineEditMailingListName.text().toUtf8())) - self.setCurrentItemColor(QtGui.QColor(137, 04, 177)) #magenta - self.rerenderComboBoxSendFrom() - self.rerenderComboBoxSendFromBroadcast() - BMConfigParser().save() - self.rerenderMessagelistToLabels() + dialogs.SpecialAddressBehaviorDialog(self, BMConfigParser()) def on_action_EmailGatewayDialog(self): self.dialog = EmailGatewayDialog(self) @@ -4190,33 +4165,6 @@ class settingsDialog(QtGui.QDialog): self.parent.ui.pushButtonFetchNamecoinID.show() -class SpecialAddressBehaviorDialog(QtGui.QDialog): - - def __init__(self, parent): - QtGui.QWidget.__init__(self, parent) - self.ui = Ui_SpecialAddressBehaviorDialog() - self.ui.setupUi(self) - self.parent = parent - addressAtCurrentRow = parent.getCurrentAccount() - if not BMConfigParser().safeGetBoolean(addressAtCurrentRow, 'chan'): - if BMConfigParser().safeGetBoolean(addressAtCurrentRow, 'mailinglist'): - self.ui.radioButtonBehaviorMailingList.click() - else: - self.ui.radioButtonBehaveNormalAddress.click() - try: - mailingListName = BMConfigParser().get( - addressAtCurrentRow, 'mailinglistname') - except: - mailingListName = '' - self.ui.lineEditMailingListName.setText( - unicode(mailingListName, 'utf-8')) - else: # if addressAtCurrentRow is a chan address - self.ui.radioButtonBehaviorMailingList.setDisabled(True) - self.ui.lineEditMailingListName.setText(_translate( - "MainWindow", "This is a chan address. You cannot use it as a pseudo-mailing list.")) - - QtGui.QWidget.resize(self, QtGui.QWidget.sizeHint(self)) - class EmailGatewayDialog(QtGui.QDialog): def __init__(self, parent): diff --git a/src/bitmessageqt/dialogs.py b/src/bitmessageqt/dialogs.py index 3b2cce2d..5e0d9718 100644 --- a/src/bitmessageqt/dialogs.py +++ b/src/bitmessageqt/dialogs.py @@ -145,3 +145,68 @@ class ConnectDialog(QtGui.QDialog, RetranslateMixin): super(ConnectDialog, self).__init__(parent) widgets.load('connect.ui', self) QtGui.QWidget.resize(self, QtGui.QWidget.sizeHint(self)) + + +class SpecialAddressBehaviorDialog(QtGui.QDialog, RetranslateMixin): + + def __init__(self, parent=None, config=None): + super(SpecialAddressBehaviorDialog, self).__init__(parent) + widgets.load('specialaddressbehavior.ui', self) + self.address = parent.getCurrentAccount() + self.parent = parent + self.config = config + + try: + self.address_is_chan = config.safeGetBoolean( + self.address, 'chan' + ) + except AttributeError: + pass + else: + if self.address_is_chan: # address is a chan address + self.radioButtonBehaviorMailingList.setDisabled(True) + self.lineEditMailingListName.setText(_translate( + "MainWindow", + "This is a chan address. You cannot use it as a" + " pseudo-mailing list." + )) + else: + if config.safeGetBoolean(self.address, 'mailinglist'): + self.radioButtonBehaviorMailingList.click() + else: + self.radioButtonBehaveNormalAddress.click() + try: + mailingListName = config.get( + self.address, 'mailinglistname') + except: + mailingListName = '' + self.lineEditMailingListName.setText( + unicode(mailingListName, 'utf-8') + ) + + QtGui.QWidget.resize(self, QtGui.QWidget.sizeHint(self)) + self.show() + + def accept(self): + self.hide() + if self.address_is_chan: + return + if self.radioButtonBehaveNormalAddress.isChecked(): + self.config.set(str(self.address), 'mailinglist', 'false') + # Set the color to either black or grey + if self.config.getboolean(self.address, 'enabled'): + self.parent.setCurrentItemColor( + QtGui.QApplication.palette().text().color() + ) + else: + self.parent.setCurrentItemColor(QtGui.QColor(128, 128, 128)) + else: + self.config.set(str(self.address), 'mailinglist', 'true') + self.config.set(str(self.address), 'mailinglistname', str( + self.lineEditMailingListName.text().toUtf8())) + self.parent.setCurrentItemColor( + QtGui.QColor(137, 04, 177)) # magenta + self.parent.rerenderComboBoxSendFrom() + self.parent.rerenderComboBoxSendFromBroadcast() + self.config.save() + self.parent.rerenderMessagelistToLabels() diff --git a/src/bitmessageqt/specialaddressbehavior.py b/src/bitmessageqt/specialaddressbehavior.py deleted file mode 100644 index 78ff890d..00000000 --- a/src/bitmessageqt/specialaddressbehavior.py +++ /dev/null @@ -1,64 +0,0 @@ -# -*- coding: utf-8 -*- - -# Form implementation generated from reading ui file 'specialaddressbehavior.ui' -# -# Created: Fri Apr 26 17:43:31 2013 -# by: PyQt4 UI code generator 4.9.4 -# -# WARNING! All changes made in this file will be lost! - -from PyQt4 import QtCore, QtGui - -try: - _fromUtf8 = QtCore.QString.fromUtf8 -except AttributeError: - _fromUtf8 = lambda s: s - -class Ui_SpecialAddressBehaviorDialog(object): - def setupUi(self, SpecialAddressBehaviorDialog): - SpecialAddressBehaviorDialog.setObjectName(_fromUtf8("SpecialAddressBehaviorDialog")) - SpecialAddressBehaviorDialog.resize(386, 172) - self.gridLayout = QtGui.QGridLayout(SpecialAddressBehaviorDialog) - self.gridLayout.setObjectName(_fromUtf8("gridLayout")) - self.radioButtonBehaveNormalAddress = QtGui.QRadioButton(SpecialAddressBehaviorDialog) - self.radioButtonBehaveNormalAddress.setChecked(True) - self.radioButtonBehaveNormalAddress.setObjectName(_fromUtf8("radioButtonBehaveNormalAddress")) - self.gridLayout.addWidget(self.radioButtonBehaveNormalAddress, 0, 0, 1, 1) - self.radioButtonBehaviorMailingList = QtGui.QRadioButton(SpecialAddressBehaviorDialog) - self.radioButtonBehaviorMailingList.setObjectName(_fromUtf8("radioButtonBehaviorMailingList")) - self.gridLayout.addWidget(self.radioButtonBehaviorMailingList, 1, 0, 1, 1) - self.label = QtGui.QLabel(SpecialAddressBehaviorDialog) - self.label.setWordWrap(True) - self.label.setObjectName(_fromUtf8("label")) - self.gridLayout.addWidget(self.label, 2, 0, 1, 1) - self.label_2 = QtGui.QLabel(SpecialAddressBehaviorDialog) - self.label_2.setObjectName(_fromUtf8("label_2")) - self.gridLayout.addWidget(self.label_2, 3, 0, 1, 1) - self.lineEditMailingListName = QtGui.QLineEdit(SpecialAddressBehaviorDialog) - self.lineEditMailingListName.setEnabled(False) - self.lineEditMailingListName.setObjectName(_fromUtf8("lineEditMailingListName")) - self.gridLayout.addWidget(self.lineEditMailingListName, 4, 0, 1, 1) - self.buttonBox = QtGui.QDialogButtonBox(SpecialAddressBehaviorDialog) - self.buttonBox.setMinimumSize(QtCore.QSize(368, 0)) - self.buttonBox.setOrientation(QtCore.Qt.Horizontal) - self.buttonBox.setStandardButtons(QtGui.QDialogButtonBox.Cancel|QtGui.QDialogButtonBox.Ok) - self.buttonBox.setObjectName(_fromUtf8("buttonBox")) - self.gridLayout.addWidget(self.buttonBox, 5, 0, 1, 1) - - self.retranslateUi(SpecialAddressBehaviorDialog) - QtCore.QObject.connect(self.buttonBox, QtCore.SIGNAL(_fromUtf8("accepted()")), SpecialAddressBehaviorDialog.accept) - QtCore.QObject.connect(self.buttonBox, QtCore.SIGNAL(_fromUtf8("rejected()")), SpecialAddressBehaviorDialog.reject) - QtCore.QObject.connect(self.radioButtonBehaviorMailingList, QtCore.SIGNAL(_fromUtf8("clicked(bool)")), self.lineEditMailingListName.setEnabled) - QtCore.QObject.connect(self.radioButtonBehaveNormalAddress, QtCore.SIGNAL(_fromUtf8("clicked(bool)")), self.lineEditMailingListName.setDisabled) - QtCore.QMetaObject.connectSlotsByName(SpecialAddressBehaviorDialog) - SpecialAddressBehaviorDialog.setTabOrder(self.radioButtonBehaveNormalAddress, self.radioButtonBehaviorMailingList) - SpecialAddressBehaviorDialog.setTabOrder(self.radioButtonBehaviorMailingList, self.lineEditMailingListName) - SpecialAddressBehaviorDialog.setTabOrder(self.lineEditMailingListName, self.buttonBox) - - def retranslateUi(self, SpecialAddressBehaviorDialog): - SpecialAddressBehaviorDialog.setWindowTitle(QtGui.QApplication.translate("SpecialAddressBehaviorDialog", "Special Address Behavior", None, QtGui.QApplication.UnicodeUTF8)) - self.radioButtonBehaveNormalAddress.setText(QtGui.QApplication.translate("SpecialAddressBehaviorDialog", "Behave as a normal address", None, QtGui.QApplication.UnicodeUTF8)) - self.radioButtonBehaviorMailingList.setText(QtGui.QApplication.translate("SpecialAddressBehaviorDialog", "Behave as a pseudo-mailing-list address", None, QtGui.QApplication.UnicodeUTF8)) - self.label.setText(QtGui.QApplication.translate("SpecialAddressBehaviorDialog", "Mail received to a pseudo-mailing-list address will be automatically broadcast to subscribers (and thus will be public).", None, QtGui.QApplication.UnicodeUTF8)) - self.label_2.setText(QtGui.QApplication.translate("SpecialAddressBehaviorDialog", "Name of the pseudo-mailing-list:", None, QtGui.QApplication.UnicodeUTF8)) - From f3808212b5547d709f0de085d583f64c4603772f Mon Sep 17 00:00:00 2001 From: Dmitri Bogomolov <4glitch@gmail.com> Date: Wed, 18 Oct 2017 18:42:59 +0300 Subject: [PATCH 337/407] Added link to github into labelVersion in AboutDialog --- src/bitmessageqt/about.ui | 2 +- src/bitmessageqt/dialogs.py | 12 ++++++++---- 2 files changed, 9 insertions(+), 5 deletions(-) diff --git a/src/bitmessageqt/about.ui b/src/bitmessageqt/about.ui index d09cbc4d..099875c0 100644 --- a/src/bitmessageqt/about.ui +++ b/src/bitmessageqt/about.ui @@ -39,7 +39,7 @@ - PyBitmessage + <html><head/><body><p><a href="https://github.com/Bitmessage/PyBitmessage/tree/:branch:"><span style="text-decoration:none; color:#0000ff;">PyBitmessage :version:</span></a></p></body></html> Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter diff --git a/src/bitmessageqt/dialogs.py b/src/bitmessageqt/dialogs.py index 5e0d9718..0fffd01a 100644 --- a/src/bitmessageqt/dialogs.py +++ b/src/bitmessageqt/dialogs.py @@ -114,11 +114,15 @@ class AboutDialog(QtGui.QDialog, RetranslateMixin): super(AboutDialog, self).__init__(parent) widgets.load('about.ui', self) commit = paths.lastCommit()[:7] - label = "PyBitmessage " + softwareVersion + version = softwareVersion if commit: - label += '-' + commit - self.labelVersion.setText(label) - QtGui.QWidget.resize(self, QtGui.QWidget.sizeHint(self)) + version += '-' + commit + self.labelVersion.setText( + self.labelVersion.text().replace( + ':version:', version + ).replace(':branch:', commit or 'v%s' % version) + ) + self.labelVersion.setOpenExternalLinks(True) class IconGlossaryDialog(QtGui.QDialog, RetranslateMixin): From b9cd571d9b1d93c6cea37917d58b2d0b7fbe6890 Mon Sep 17 00:00:00 2001 From: Dmitri Bogomolov <4glitch@gmail.com> Date: Fri, 3 Nov 2017 14:28:48 +0200 Subject: [PATCH 338/407] Hide redundant QGroupBox title --- src/bitmessageqt/dialogs.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/bitmessageqt/dialogs.py b/src/bitmessageqt/dialogs.py index 0fffd01a..ce1702df 100644 --- a/src/bitmessageqt/dialogs.py +++ b/src/bitmessageqt/dialogs.py @@ -130,6 +130,9 @@ class IconGlossaryDialog(QtGui.QDialog, RetranslateMixin): super(IconGlossaryDialog, self).__init__(parent) widgets.load('iconglossary.ui', self) + # FIXME: check the window title visibility here + self.groupBox.setTitle('') + self.labelPortNumber.setText(_translate( "iconGlossaryDialog", "You are using TCP port %1. (This can be changed in the settings)." From 20288d4ab420470ab573b470f22c0fbcfe95f77a Mon Sep 17 00:00:00 2001 From: Dmitri Bogomolov <4glitch@gmail.com> Date: Tue, 7 Nov 2017 13:46:23 +0200 Subject: [PATCH 339/407] Try to replace copyright year from last commit date --- src/bitmessageqt/dialogs.py | 15 +++++++++++++-- src/bitmessageqt/support.py | 4 ++-- src/paths.py | 17 ++++++++++++----- 3 files changed, 27 insertions(+), 9 deletions(-) diff --git a/src/bitmessageqt/dialogs.py b/src/bitmessageqt/dialogs.py index ce1702df..ef22fd3b 100644 --- a/src/bitmessageqt/dialogs.py +++ b/src/bitmessageqt/dialogs.py @@ -113,10 +113,11 @@ class AboutDialog(QtGui.QDialog, RetranslateMixin): def __init__(self, parent=None): super(AboutDialog, self).__init__(parent) widgets.load('about.ui', self) - commit = paths.lastCommit()[:7] + last_commit = paths.lastCommit() version = softwareVersion + commit = last_commit.get('commit') if commit: - version += '-' + commit + version += '-' + commit[:7] self.labelVersion.setText( self.labelVersion.text().replace( ':version:', version @@ -124,6 +125,16 @@ class AboutDialog(QtGui.QDialog, RetranslateMixin): ) self.labelVersion.setOpenExternalLinks(True) + try: + self.label_2.setText( + self.label_2.text().replace( + '2017', str(last_commit.get('time').year) + )) + except AttributeError: + pass + + QtGui.QWidget.resize(self, QtGui.QWidget.sizeHint(self)) + class IconGlossaryDialog(QtGui.QDialog, RetranslateMixin): def __init__(self, parent=None, config=None): diff --git a/src/bitmessageqt/support.py b/src/bitmessageqt/support.py index 03b302e6..db690a2b 100644 --- a/src/bitmessageqt/support.py +++ b/src/bitmessageqt/support.py @@ -85,9 +85,9 @@ def createSupportMessage(myapp): return myapp.ui.comboBoxSendFrom.setCurrentIndex(addrIndex) myapp.ui.lineEditTo.setText(SUPPORT_ADDRESS) - + version = softwareVersion - commit = paths.lastCommit() + commit = paths.lastCommit().get('commit') if commit: version += " GIT " + commit diff --git a/src/paths.py b/src/paths.py index 0f843edf..325fcd8b 100644 --- a/src/paths.py +++ b/src/paths.py @@ -1,5 +1,7 @@ from os import environ, path import sys +import re +from datetime import datetime # When using py2exe or py2app, the variable frozen is added to the sys # namespace. This can be used to setup a different code path for @@ -95,13 +97,18 @@ def tail(f, lines=20): all_read_text = ''.join(reversed(blocks)) return '\n'.join(all_read_text.splitlines()[-total_lines_wanted:]) + def lastCommit(): githeadfile = path.join(codePath(), '..', '.git', 'logs', 'HEAD') - version = "" - if (path.isfile(githeadfile)): + result = {} + if path.isfile(githeadfile): try: with open(githeadfile, 'rt') as githead: - version = tail(githead, 1).split()[1] - except IOError: + line = tail(githead, 1) + result['commit'] = line.split()[1] + result['time'] = datetime.fromtimestamp( + float(re.search(r'>\s*(.*?)\s', line).group(1)) + ) + except (IOError, AttributeError, TypeError): pass - return version + return result From b899086d917433e13962f8cb2b458c58f811a63d Mon Sep 17 00:00:00 2001 From: Dmitri Bogomolov <4glitch@gmail.com> Date: Thu, 18 Jan 2018 16:14:29 +0200 Subject: [PATCH 340/407] Followed the recommendation #527 about tab indexes --- src/bitmessageqt/__init__.py | 82 ++++++++++++++++++++++--------- src/bitmessageqt/bitmessageui.py | 8 ++- src/bitmessageqt/messageview.py | 19 ++++--- src/bitmessageqt/newchandialog.py | 4 +- src/bitmessageqt/support.py | 8 ++- 5 files changed, 86 insertions(+), 35 deletions(-) diff --git a/src/bitmessageqt/__init__.py b/src/bitmessageqt/__init__.py index 4cb388a4..60e1b05e 100644 --- a/src/bitmessageqt/__init__.py +++ b/src/bitmessageqt/__init__.py @@ -884,7 +884,9 @@ class MyForm(settingsmixin.SMainWindow): def appIndicatorInbox(self, item=None): self.appIndicatorShow() # select inbox - self.ui.tabWidget.setCurrentIndex(0) + self.ui.tabWidget.setCurrentIndex( + self.ui.tabWidget.indexOf(self.ui.inbox) + ) self.ui.treeWidgetYourIdentities.setCurrentItem( self.ui.treeWidgetYourIdentities.topLevelItem(0).child(0) ) @@ -898,18 +900,24 @@ class MyForm(settingsmixin.SMainWindow): # Show the program window and select send tab def appIndicatorSend(self): self.appIndicatorShow() - self.ui.tabWidget.setCurrentIndex(1) + self.ui.tabWidget.setCurrentIndex( + self.ui.tabWidget.indexOf(self.ui.send) + ) # Show the program window and select subscriptions tab def appIndicatorSubscribe(self): self.appIndicatorShow() - self.ui.tabWidget.setCurrentIndex(2) + self.ui.tabWidget.setCurrentIndex( + self.ui.tabWidget.indexOf(self.ui.subscriptions) + ) # Show the program window and select channels tab def appIndicatorChannel(self): self.appIndicatorShow() - self.ui.tabWidget.setCurrentIndex(3) - + self.ui.tabWidget.setCurrentIndex( + self.ui.tabWidget.indexOf(self.ui.chans) + ) + def propagateUnreadCount(self, address = None, folder = "inbox", widget = None, type = 1): widgets = [self.ui.treeWidgetYourIdentities, self.ui.treeWidgetSubscriptions, self.ui.treeWidgetChans] queryReturn = sqlQuery("SELECT toaddress, folder, COUNT(msgid) AS cnt FROM inbox WHERE read = 0 GROUP BY toaddress, folder") @@ -1373,8 +1381,12 @@ class MyForm(settingsmixin.SMainWindow): currentAddress = self.getCurrentAccount() if currentAddress: self.setSendFromComboBox(currentAddress) - self.ui.tabWidgetSend.setCurrentIndex(0) - self.ui.tabWidget.setCurrentIndex(1) + self.ui.tabWidgetSend.setCurrentIndex( + self.ui.tabWidgetSend.indexOf(self.ui.sendDirect) + ) + self.ui.tabWidget.setCurrentIndex( + self.ui.tabWidget.indexOf(self.ui.send) + ) self.ui.lineEditTo.setFocus() event.ignore() elif event.key() == QtCore.Qt.Key_F: @@ -1455,7 +1467,9 @@ class MyForm(settingsmixin.SMainWindow): return queues.addressGeneratorQueue.put(('createDeterministicAddresses', addressVersionNumber, streamNumberForAddress, "regenerated deterministic address", self.regenerateAddressesDialogInstance.ui.spinBoxNumberOfAddressesToMake.value( ), self.regenerateAddressesDialogInstance.ui.lineEditPassphrase.text().toUtf8(), self.regenerateAddressesDialogInstance.ui.checkBoxEighteenByteRipe.isChecked())) - self.ui.tabWidget.setCurrentIndex(3) + self.ui.tabWidget.setCurrentIndex( + self.ui.tabWidget.indexOf(self.ui.chans) + ) # opens 'join chan' dialog def click_actionJoinChan(self): @@ -1775,7 +1789,8 @@ class MyForm(settingsmixin.SMainWindow): self.statusBar().clearMessage() - if self.ui.tabWidgetSend.currentIndex() == 0: + if self.ui.tabWidgetSend.currentIndex() == \ + self.ui.tabWidgetSend.indexOf(self.ui.sendDirect): # message to specific people sendMessageToPeople = True fromAddress = str(self.ui.comboBoxSendFrom.itemData( @@ -1979,7 +1994,9 @@ class MyForm(settingsmixin.SMainWindow): self.ui.comboBoxSendFromBroadcast.setCurrentIndex(0) self.ui.lineEditSubjectBroadcast.setText('') self.ui.textEditMessageBroadcast.reset() - self.ui.tabWidget.setCurrentIndex(1) + self.ui.tabWidget.setCurrentIndex( + self.ui.tabWidget.indexOf(self.ui.send) + ) self.ui.tableWidgetInboxSubscriptions.setCurrentCell(0, 0) self.statusBar().showMessage(_translate( "MainWindow", "Broadcast queued."), 10000) @@ -2009,10 +2026,12 @@ class MyForm(settingsmixin.SMainWindow): def setBroadcastEnablementDependingOnWhetherThisIsAMailingListAddress(self, address): # If this is a chan then don't let people broadcast because no one # should subscribe to chan addresses. - if BMConfigParser().safeGetBoolean(str(address), 'mailinglist'): - self.ui.tabWidgetSend.setCurrentIndex(1) - else: - self.ui.tabWidgetSend.setCurrentIndex(0) + self.ui.tabWidgetSend.setCurrentIndex( + self.ui.tabWidgetSend.indexOf( + self.ui.sendBroadcast + if BMConfigParser().safeGetBoolean(str(address), 'mailinglist') + else self.ui.sendDirect + )) def rerenderComboBoxSendFrom(self): self.ui.comboBoxSendFrom.clear() @@ -2480,8 +2499,12 @@ class MyForm(settingsmixin.SMainWindow): self.ui.lineEditTo.setText(acct.toAddress) self.ui.lineEditSubject.setText(acct.subject) self.ui.textEditMessage.setText(acct.message) - self.ui.tabWidgetSend.setCurrentIndex(0) - self.ui.tabWidget.setCurrentIndex(1) + self.ui.tabWidgetSend.setCurrentIndex( + self.ui.tabWidgetSend.indexOf(self.ui.sendDirect) + ) + self.ui.tabWidget.setCurrentIndex( + self.ui.tabWidget.indexOf(self.ui.send) + ) self.ui.textEditMessage.setFocus() elif self.dialog.ui.radioButtonRegister.isChecked(): email = str(self.dialog.ui.lineEditEmail.text().toUtf8()) @@ -2870,7 +2893,9 @@ class MyForm(settingsmixin.SMainWindow): 'message': self.ui.textEditMessage } if toAddressAtCurrentInboxRow == str_broadcast_subscribers: - self.ui.tabWidgetSend.setCurrentIndex(0) + self.ui.tabWidgetSend.setCurrentIndex( + self.ui.tabWidgetSend.indexOf(self.ui.sendDirect) + ) # toAddressAtCurrentInboxRow = fromAddressAtCurrentInboxRow elif not BMConfigParser().has_section(toAddressAtCurrentInboxRow): QtGui.QMessageBox.information(self, _translate("MainWindow", "Address is gone"), _translate( @@ -2880,13 +2905,16 @@ class MyForm(settingsmixin.SMainWindow): "MainWindow", "Error: The address from which you are trying to send is disabled. You\'ll have to enable it on the \'Your Identities\' tab before using it."), QMessageBox.Ok) else: self.setBroadcastEnablementDependingOnWhetherThisIsAMailingListAddress(toAddressAtCurrentInboxRow) - if self.ui.tabWidgetSend.currentIndex() == 1: + broadcast_tab_index = self.ui.tabWidgetSend.indexOf( + self.ui.sendBroadcast + ) + if self.ui.tabWidgetSend.currentIndex() == broadcast_tab_index: widget = { 'subject': self.ui.lineEditSubjectBroadcast, 'from': self.ui.comboBoxSendFromBroadcast, 'message': self.ui.textEditMessageBroadcast } - self.ui.tabWidgetSend.setCurrentIndex(1) + self.ui.tabWidgetSend.setCurrentIndex(broadcast_tab_index) toAddressAtCurrentInboxRow = fromAddressAtCurrentInboxRow if fromAddressAtCurrentInboxRow == tableWidget.item(currentInboxRow, 1).label or ( isinstance(acct, GatewayAccount) and fromAddressAtCurrentInboxRow == acct.relayAddress): @@ -2910,7 +2938,9 @@ class MyForm(settingsmixin.SMainWindow): widget['subject'].setText(tableWidget.item(currentInboxRow, 2).label) else: widget['subject'].setText('Re: ' + tableWidget.item(currentInboxRow, 2).label) - self.ui.tabWidget.setCurrentIndex(1) + self.ui.tabWidget.setCurrentIndex( + self.ui.tabWidget.indexOf(self.ui.send) + ) widget['message'].setFocus() def on_action_InboxAddSenderToAddressBook(self): @@ -2921,7 +2951,9 @@ class MyForm(settingsmixin.SMainWindow): # tableWidget.item(currentRow,1).data(Qt.UserRole).toPyObject() addressAtCurrentInboxRow = tableWidget.item( currentInboxRow, 1).data(Qt.UserRole) - self.ui.tabWidget.setCurrentIndex(1) + self.ui.tabWidget.setCurrentIndex( + self.ui.tabWidget.indexOf(self.ui.send) + ) dialog = dialogs.AddAddressDialog(self) dialog.lineEditAddress.setText(addressAtCurrentInboxRow) self.click_pushButtonAddAddressBook(dialog) @@ -3170,7 +3202,9 @@ class MyForm(settingsmixin.SMainWindow): "MainWindow", "No addresses selected."), 10000) else: self.statusBar().clearMessage() - self.ui.tabWidget.setCurrentIndex(1) + self.ui.tabWidget.setCurrentIndex( + self.ui.tabWidget.indexOf(self.ui.send) + ) def on_action_AddressBookSubscribe(self): listOfSelectedRows = {} @@ -3184,7 +3218,9 @@ class MyForm(settingsmixin.SMainWindow): continue labelAtCurrentRow = self.ui.tableWidgetAddressBook.item(currentRow,0).text().toUtf8() self.addSubscription(addressAtCurrentRow, labelAtCurrentRow) - self.ui.tabWidget.setCurrentIndex(2) + self.ui.tabWidget.setCurrentIndex( + self.ui.tabWidget.indexOf(self.ui.subscriptions) + ) def on_context_menuAddressBook(self, point): self.popMenuAddressBook = QtGui.QMenu(self) diff --git a/src/bitmessageqt/bitmessageui.py b/src/bitmessageqt/bitmessageui.py index 3eb04101..f5d28a7f 100644 --- a/src/bitmessageqt/bitmessageui.py +++ b/src/bitmessageqt/bitmessageui.py @@ -634,8 +634,12 @@ class Ui_MainWindow(object): self.menubar.addAction(self.menuHelp.menuAction()) self.retranslateUi(MainWindow) - self.tabWidget.setCurrentIndex(0) - self.tabWidgetSend.setCurrentIndex(0) + self.tabWidget.setCurrentIndex( + self.tabWidget.indexOf(self.inbox) + ) + self.tabWidgetSend.setCurrentIndex( + self.tabWidgetSend.indexOf(self.sendDirect) + ) QtCore.QMetaObject.connectSlotsByName(MainWindow) MainWindow.setTabOrder(self.tableWidgetInbox, self.textEditInboxMessage) MainWindow.setTabOrder(self.textEditInboxMessage, self.comboBoxSendFrom) diff --git a/src/bitmessageqt/messageview.py b/src/bitmessageqt/messageview.py index 40830a70..de357e23 100644 --- a/src/bitmessageqt/messageview.py +++ b/src/bitmessageqt/messageview.py @@ -53,15 +53,20 @@ class MessageView(QtGui.QTextBrowser): def confirmURL(self, link): if link.scheme() == "mailto": - QtGui.QApplication.activeWindow().ui.lineEditTo.setText(link.path()) + window = QtGui.QApplication.activeWindow() + window.ui.lineEditTo.setText(link.path()) if link.hasQueryItem("subject"): - QtGui.QApplication.activeWindow().ui.lineEditSubject.setText(link.queryItemValue("subject")) + window.ui.lineEditSubject.setText( + link.queryItemValue("subject")) if link.hasQueryItem("body"): - QtGui.QApplication.activeWindow().ui.textEditMessage.setText(link.queryItemValue("body")) - QtGui.QApplication.activeWindow().setSendFromComboBox() - QtGui.QApplication.activeWindow().ui.tabWidgetSend.setCurrentIndex(0) - QtGui.QApplication.activeWindow().ui.tabWidget.setCurrentIndex(1) - QtGui.QApplication.activeWindow().ui.textEditMessage.setFocus() + window.ui.textEditMessage.setText( + link.queryItemValue("body")) + window.setSendFromComboBox() + window.ui.tabWidgetSend.setCurrentIndex(0) + window.ui.tabWidget.setCurrentIndex( + window.ui.tabWidget.indexOf(window.ui.send) + ) + window.ui.textEditMessage.setFocus() return reply = QtGui.QMessageBox.warning(self, QtGui.QApplication.translate("MessageView", "Follow external link"), diff --git a/src/bitmessageqt/newchandialog.py b/src/bitmessageqt/newchandialog.py index a129c608..ed683b13 100644 --- a/src/bitmessageqt/newchandialog.py +++ b/src/bitmessageqt/newchandialog.py @@ -36,7 +36,9 @@ class NewChanDialog(QtGui.QDialog, RetranslateMixin): addressGeneratorReturnValue = apiAddressGeneratorReturnQueue.get(True) if len(addressGeneratorReturnValue) > 0 and addressGeneratorReturnValue[0] != 'chan name does not match address': UISignalQueue.put(('updateStatusBar', _translate("newchandialog", "Successfully created / joined chan %1").arg(unicode(self.chanPassPhrase.text())))) - self.parent.ui.tabWidget.setCurrentIndex(3) + self.parent.ui.tabWidget.setCurrentIndex( + self.parent.ui.tabWidget.indexOf(self.parent.ui.chans) + ) self.done(QtGui.QDialog.Accepted) else: UISignalQueue.put(('updateStatusBar', _translate("newchandialog", "Chan creation / joining failed"))) diff --git a/src/bitmessageqt/support.py b/src/bitmessageqt/support.py index db690a2b..cea5ddc8 100644 --- a/src/bitmessageqt/support.py +++ b/src/bitmessageqt/support.py @@ -129,6 +129,10 @@ def createSupportMessage(myapp): myapp.ui.textEditMessage.setText(str(QtGui.QApplication.translate("Support", SUPPORT_MESSAGE)).format(version, os, architecture, pythonversion, opensslversion, frozen, portablemode, cpow, openclpow, locale, socks, upnp, connectedhosts)) # single msg tab - myapp.ui.tabWidgetSend.setCurrentIndex(0) + myapp.ui.tabWidgetSend.setCurrentIndex( + myapp.ui.tabWidgetSend.indexOf(myapp.ui.sendDirect) + ) # send tab - myapp.ui.tabWidget.setCurrentIndex(1) + myapp.ui.tabWidget.setCurrentIndex( + myapp.ui.tabWidget.indexOf(myapp.ui.send) + ) From fe76d230eb1e9aa0ee4b2c27f2b49bcbaaf999bd Mon Sep 17 00:00:00 2001 From: Dmitri Bogomolov <4glitch@gmail.com> Date: Thu, 18 Jan 2018 18:47:09 +0200 Subject: [PATCH 341/407] Moved RegenerateAddressesDialog into dialogs module --- src/bitmessageqt/__init__.py | 63 ++++++------ src/bitmessageqt/dialogs.py | 7 ++ src/bitmessageqt/regenerateaddresses.py | 124 ------------------------ 3 files changed, 43 insertions(+), 151 deletions(-) delete mode 100644 src/bitmessageqt/regenerateaddresses.py diff --git a/src/bitmessageqt/__init__.py b/src/bitmessageqt/__init__.py index 60e1b05e..2b9daa57 100644 --- a/src/bitmessageqt/__init__.py +++ b/src/bitmessageqt/__init__.py @@ -27,7 +27,6 @@ from newaddresswizard import * from messageview import MessageView from migrationwizard import * from foldertree import * -from regenerateaddresses import * from newchandialog import * from safehtmlparser import * from emailgateway import * @@ -1442,31 +1441,50 @@ class MyForm(settingsmixin.SMainWindow): elif self.getCurrentFolder(self.ui.treeWidgetChans) == "trash": self.loadMessagelist(self.ui.tableWidgetInboxChans, self.getCurrentAccount(self.ui.treeWidgetChans), "trash") - - # menu botton 'regenerate deterministic addresses' + # menu button 'regenerate deterministic addresses' def click_actionRegenerateDeterministicAddresses(self): - self.regenerateAddressesDialogInstance = regenerateAddressesDialog( - self) - if self.regenerateAddressesDialogInstance.exec_(): - if self.regenerateAddressesDialogInstance.ui.lineEditPassphrase.text() == "": - QMessageBox.about(self, _translate("MainWindow", "bad passphrase"), _translate( - "MainWindow", "You must type your passphrase. If you don\'t have one then this is not the form for you.")) + dialog = dialogs.RegenerateAddressesDialog(self) + if dialog.exec_(): + if dialog.lineEditPassphrase.text() == "": + QMessageBox.about( + self, _translate("MainWindow", "bad passphrase"), + _translate( + "MainWindow", + "You must type your passphrase. If you don\'t" + " have one then this is not the form for you." + )) return - streamNumberForAddress = int( - self.regenerateAddressesDialogInstance.ui.lineEditStreamNumber.text()) + streamNumberForAddress = int(dialog.lineEditStreamNumber.text()) try: addressVersionNumber = int( - self.regenerateAddressesDialogInstance.ui.lineEditAddressVersionNumber.text()) + dialog.lineEditAddressVersionNumber.text()) except: - QMessageBox.about(self, _translate("MainWindow", "Bad address version number"), _translate( - "MainWindow", "Your address version number must be a number: either 3 or 4.")) + QMessageBox.about( + self, + _translate("MainWindow", "Bad address version number"), + _translate( + "MainWindow", + "Your address version number must be a number:" + " either 3 or 4." + )) return if addressVersionNumber < 3 or addressVersionNumber > 4: - QMessageBox.about(self, _translate("MainWindow", "Bad address version number"), _translate( - "MainWindow", "Your address version number must be either 3 or 4.")) + QMessageBox.about( + self, + _translate("MainWindow", "Bad address version number"), + _translate( + "MainWindow", + "Your address version number must be either 3 or 4." + )) return - queues.addressGeneratorQueue.put(('createDeterministicAddresses', addressVersionNumber, streamNumberForAddress, "regenerated deterministic address", self.regenerateAddressesDialogInstance.ui.spinBoxNumberOfAddressesToMake.value( - ), self.regenerateAddressesDialogInstance.ui.lineEditPassphrase.text().toUtf8(), self.regenerateAddressesDialogInstance.ui.checkBoxEighteenByteRipe.isChecked())) + queues.addressGeneratorQueue.put(( + 'createDeterministicAddresses', + addressVersionNumber, streamNumberForAddress, + "regenerated deterministic address", + dialog.spinBoxNumberOfAddressesToMake.value(), + dialog.lineEditPassphrase.text().toUtf8(), + dialog.checkBoxEighteenByteRipe.isChecked() + )) self.ui.tabWidget.setCurrentIndex( self.ui.tabWidget.indexOf(self.ui.chans) ) @@ -3959,15 +3977,6 @@ class MyForm(settingsmixin.SMainWindow): obj.loadSettings() -class regenerateAddressesDialog(QtGui.QDialog): - - def __init__(self, parent): - QtGui.QWidget.__init__(self, parent) - self.ui = Ui_regenerateAddressesDialog() - self.ui.setupUi(self) - self.parent = parent - QtGui.QWidget.resize(self, QtGui.QWidget.sizeHint(self)) - class settingsDialog(QtGui.QDialog): def __init__(self, parent): diff --git a/src/bitmessageqt/dialogs.py b/src/bitmessageqt/dialogs.py index ef22fd3b..58003f3a 100644 --- a/src/bitmessageqt/dialogs.py +++ b/src/bitmessageqt/dialogs.py @@ -109,6 +109,13 @@ class NewSubscriptionDialog( ).arg(count)) +class RegenerateAddressesDialog(QtGui.QDialog, RetranslateMixin): + def __init__(self, parent=None): + super(RegenerateAddressesDialog, self).__init__(parent) + widgets.load('regenerateaddresses.ui', self) + QtGui.QWidget.resize(self, QtGui.QWidget.sizeHint(self)) + + class AboutDialog(QtGui.QDialog, RetranslateMixin): def __init__(self, parent=None): super(AboutDialog, self).__init__(parent) diff --git a/src/bitmessageqt/regenerateaddresses.py b/src/bitmessageqt/regenerateaddresses.py deleted file mode 100644 index 7129b632..00000000 --- a/src/bitmessageqt/regenerateaddresses.py +++ /dev/null @@ -1,124 +0,0 @@ -# -*- coding: utf-8 -*- - -# Form implementation generated from reading ui file 'regenerateaddresses.ui' -# -# Created: Sun Sep 15 23:50:23 2013 -# by: PyQt4 UI code generator 4.10.2 -# -# WARNING! All changes made in this file will be lost! - -from PyQt4 import QtCore, QtGui - -try: - _fromUtf8 = QtCore.QString.fromUtf8 -except AttributeError: - def _fromUtf8(s): - return s - -try: - _encoding = QtGui.QApplication.UnicodeUTF8 - def _translate(context, text, disambig): - return QtGui.QApplication.translate(context, text, disambig, _encoding) -except AttributeError: - def _translate(context, text, disambig): - return QtGui.QApplication.translate(context, text, disambig) - -class Ui_regenerateAddressesDialog(object): - def setupUi(self, regenerateAddressesDialog): - regenerateAddressesDialog.setObjectName(_fromUtf8("regenerateAddressesDialog")) - regenerateAddressesDialog.resize(532, 332) - self.gridLayout_2 = QtGui.QGridLayout(regenerateAddressesDialog) - self.gridLayout_2.setObjectName(_fromUtf8("gridLayout_2")) - self.buttonBox = QtGui.QDialogButtonBox(regenerateAddressesDialog) - self.buttonBox.setOrientation(QtCore.Qt.Horizontal) - self.buttonBox.setStandardButtons(QtGui.QDialogButtonBox.Cancel|QtGui.QDialogButtonBox.Ok) - self.buttonBox.setObjectName(_fromUtf8("buttonBox")) - self.gridLayout_2.addWidget(self.buttonBox, 1, 0, 1, 1) - self.groupBox = QtGui.QGroupBox(regenerateAddressesDialog) - self.groupBox.setObjectName(_fromUtf8("groupBox")) - self.gridLayout = QtGui.QGridLayout(self.groupBox) - self.gridLayout.setObjectName(_fromUtf8("gridLayout")) - self.label_6 = QtGui.QLabel(self.groupBox) - self.label_6.setObjectName(_fromUtf8("label_6")) - self.gridLayout.addWidget(self.label_6, 1, 0, 1, 1) - self.lineEditPassphrase = QtGui.QLineEdit(self.groupBox) - self.lineEditPassphrase.setInputMethodHints(QtCore.Qt.ImhHiddenText|QtCore.Qt.ImhNoAutoUppercase|QtCore.Qt.ImhNoPredictiveText) - self.lineEditPassphrase.setEchoMode(QtGui.QLineEdit.Password) - self.lineEditPassphrase.setObjectName(_fromUtf8("lineEditPassphrase")) - self.gridLayout.addWidget(self.lineEditPassphrase, 2, 0, 1, 5) - self.label_11 = QtGui.QLabel(self.groupBox) - self.label_11.setObjectName(_fromUtf8("label_11")) - self.gridLayout.addWidget(self.label_11, 3, 0, 1, 3) - self.spinBoxNumberOfAddressesToMake = QtGui.QSpinBox(self.groupBox) - sizePolicy = QtGui.QSizePolicy(QtGui.QSizePolicy.Fixed, QtGui.QSizePolicy.Fixed) - sizePolicy.setHorizontalStretch(0) - sizePolicy.setVerticalStretch(0) - sizePolicy.setHeightForWidth(self.spinBoxNumberOfAddressesToMake.sizePolicy().hasHeightForWidth()) - self.spinBoxNumberOfAddressesToMake.setSizePolicy(sizePolicy) - self.spinBoxNumberOfAddressesToMake.setMinimum(1) - self.spinBoxNumberOfAddressesToMake.setProperty("value", 8) - self.spinBoxNumberOfAddressesToMake.setObjectName(_fromUtf8("spinBoxNumberOfAddressesToMake")) - self.gridLayout.addWidget(self.spinBoxNumberOfAddressesToMake, 3, 3, 1, 1) - spacerItem = QtGui.QSpacerItem(132, 20, QtGui.QSizePolicy.Expanding, QtGui.QSizePolicy.Minimum) - self.gridLayout.addItem(spacerItem, 3, 4, 1, 1) - self.label_2 = QtGui.QLabel(self.groupBox) - self.label_2.setObjectName(_fromUtf8("label_2")) - self.gridLayout.addWidget(self.label_2, 4, 0, 1, 1) - self.lineEditAddressVersionNumber = QtGui.QLineEdit(self.groupBox) - self.lineEditAddressVersionNumber.setEnabled(True) - sizePolicy = QtGui.QSizePolicy(QtGui.QSizePolicy.Fixed, QtGui.QSizePolicy.Fixed) - sizePolicy.setHorizontalStretch(0) - sizePolicy.setVerticalStretch(0) - sizePolicy.setHeightForWidth(self.lineEditAddressVersionNumber.sizePolicy().hasHeightForWidth()) - self.lineEditAddressVersionNumber.setSizePolicy(sizePolicy) - self.lineEditAddressVersionNumber.setMaximumSize(QtCore.QSize(31, 16777215)) - self.lineEditAddressVersionNumber.setText(_fromUtf8("")) - self.lineEditAddressVersionNumber.setObjectName(_fromUtf8("lineEditAddressVersionNumber")) - self.gridLayout.addWidget(self.lineEditAddressVersionNumber, 4, 1, 1, 1) - spacerItem1 = QtGui.QSpacerItem(40, 20, QtGui.QSizePolicy.Expanding, QtGui.QSizePolicy.Minimum) - self.gridLayout.addItem(spacerItem1, 4, 2, 1, 1) - self.label_3 = QtGui.QLabel(self.groupBox) - self.label_3.setObjectName(_fromUtf8("label_3")) - self.gridLayout.addWidget(self.label_3, 5, 0, 1, 1) - self.lineEditStreamNumber = QtGui.QLineEdit(self.groupBox) - self.lineEditStreamNumber.setEnabled(False) - sizePolicy = QtGui.QSizePolicy(QtGui.QSizePolicy.Ignored, QtGui.QSizePolicy.Fixed) - sizePolicy.setHorizontalStretch(0) - sizePolicy.setVerticalStretch(0) - sizePolicy.setHeightForWidth(self.lineEditStreamNumber.sizePolicy().hasHeightForWidth()) - self.lineEditStreamNumber.setSizePolicy(sizePolicy) - self.lineEditStreamNumber.setMaximumSize(QtCore.QSize(31, 16777215)) - self.lineEditStreamNumber.setObjectName(_fromUtf8("lineEditStreamNumber")) - self.gridLayout.addWidget(self.lineEditStreamNumber, 5, 1, 1, 1) - spacerItem2 = QtGui.QSpacerItem(325, 20, QtGui.QSizePolicy.Expanding, QtGui.QSizePolicy.Minimum) - self.gridLayout.addItem(spacerItem2, 5, 2, 1, 3) - self.checkBoxEighteenByteRipe = QtGui.QCheckBox(self.groupBox) - self.checkBoxEighteenByteRipe.setObjectName(_fromUtf8("checkBoxEighteenByteRipe")) - self.gridLayout.addWidget(self.checkBoxEighteenByteRipe, 6, 0, 1, 5) - self.label_4 = QtGui.QLabel(self.groupBox) - self.label_4.setWordWrap(True) - self.label_4.setObjectName(_fromUtf8("label_4")) - self.gridLayout.addWidget(self.label_4, 7, 0, 1, 5) - self.label = QtGui.QLabel(self.groupBox) - self.label.setWordWrap(True) - self.label.setObjectName(_fromUtf8("label")) - self.gridLayout.addWidget(self.label, 0, 0, 1, 5) - self.gridLayout_2.addWidget(self.groupBox, 0, 0, 1, 1) - - self.retranslateUi(regenerateAddressesDialog) - QtCore.QObject.connect(self.buttonBox, QtCore.SIGNAL(_fromUtf8("accepted()")), regenerateAddressesDialog.accept) - QtCore.QObject.connect(self.buttonBox, QtCore.SIGNAL(_fromUtf8("rejected()")), regenerateAddressesDialog.reject) - QtCore.QMetaObject.connectSlotsByName(regenerateAddressesDialog) - - def retranslateUi(self, regenerateAddressesDialog): - regenerateAddressesDialog.setWindowTitle(_translate("regenerateAddressesDialog", "Regenerate Existing Addresses", None)) - self.groupBox.setTitle(_translate("regenerateAddressesDialog", "Regenerate existing addresses", None)) - self.label_6.setText(_translate("regenerateAddressesDialog", "Passphrase", None)) - self.label_11.setText(_translate("regenerateAddressesDialog", "Number of addresses to make based on your passphrase:", None)) - self.label_2.setText(_translate("regenerateAddressesDialog", "Address version number:", None)) - self.label_3.setText(_translate("regenerateAddressesDialog", "Stream number:", None)) - self.lineEditStreamNumber.setText(_translate("regenerateAddressesDialog", "1", None)) - self.checkBoxEighteenByteRipe.setText(_translate("regenerateAddressesDialog", "Spend several minutes of extra computing time to make the address(es) 1 or 2 characters shorter", None)) - self.label_4.setText(_translate("regenerateAddressesDialog", "You must check (or not check) this box just like you did (or didn\'t) when you made your addresses the first time.", None)) - self.label.setText(_translate("regenerateAddressesDialog", "If you have previously made deterministic addresses but lost them due to an accident (like hard drive failure), you can regenerate them here. If you used the random number generator to make your addresses then this form will be of no use to you.", None)) - From 8109fa7ecef43618302b8f17fe47e6645d002e72 Mon Sep 17 00:00:00 2001 From: Dmitri Bogomolov <4glitch@gmail.com> Date: Fri, 19 Jan 2018 16:26:07 +0200 Subject: [PATCH 342/407] Moved EmailGatewayDialog (with both usage) into dialogs module --- src/bitmessageqt/__init__.py | 138 +++++++++---------------------- src/bitmessageqt/dialogs.py | 77 +++++++++++++++++ src/bitmessageqt/emailgateway.py | 103 ----------------------- src/bitmessageqt/emailgateway.ui | 85 +++++++++++++++++-- 4 files changed, 191 insertions(+), 212 deletions(-) delete mode 100644 src/bitmessageqt/emailgateway.py diff --git a/src/bitmessageqt/__init__.py b/src/bitmessageqt/__init__.py index 2b9daa57..197d9d21 100644 --- a/src/bitmessageqt/__init__.py +++ b/src/bitmessageqt/__init__.py @@ -29,7 +29,6 @@ from migrationwizard import * from foldertree import * from newchandialog import * from safehtmlparser import * -from emailgateway import * from settings import * import settingsmixin import support @@ -2155,20 +2154,22 @@ class MyForm(settingsmixin.SMainWindow): # whether it's in current message list or not self.indicatorUpdate(True, to_label=acct.toLabel) # cannot find item to pass here ): - if hasattr(acct, "feedback") and acct.feedback != GatewayAccount.ALL_OK: + if hasattr(acct, "feedback") \ + and acct.feedback != GatewayAccount.ALL_OK: if acct.feedback == GatewayAccount.REGISTRATION_DENIED: - self.dialog = EmailGatewayRegistrationDialog(self, _translate("EmailGatewayRegistrationDialog", "Registration failed:"), - _translate("EmailGatewayRegistrationDialog", "The requested email address is not available, please try a new one. Fill out the new desired email address (including @mailchuck.com) below:") - ) - if self.dialog.exec_(): - email = str(self.dialog.ui.lineEditEmail.text().toUtf8()) - # register resets address variables - acct.register(email) - BMConfigParser().set(acct.fromAddress, 'label', email) - BMConfigParser().set(acct.fromAddress, 'gateway', 'mailchuck') - BMConfigParser().save() - self.statusBar().showMessage(_translate( - "MainWindow", "Sending email gateway registration request"), 10000) + dialog = dialogs.EmailGatewayDialog( + self, + _translate( + "EmailGatewayRegistrationDialog", + "Registration failed:"), + _translate( + "EmailGatewayRegistrationDialog", + "The requested email address is not available," + " please try a new one."), + config=BMConfigParser() + ) + if dialog.exec_(): + dialog.register(acct) def click_pushButtonAddAddressBook(self, dialog=None): if not dialog: @@ -2488,56 +2489,31 @@ class MyForm(settingsmixin.SMainWindow): dialogs.SpecialAddressBehaviorDialog(self, BMConfigParser()) def on_action_EmailGatewayDialog(self): - self.dialog = EmailGatewayDialog(self) + dialog = dialogs.EmailGatewayDialog(self, config=BMConfigParser()) # For Modal dialogs - if self.dialog.exec_(): - addressAtCurrentRow = self.getCurrentAccount() - acct = accountClass(addressAtCurrentRow) - # no chans / mailinglists - if acct.type != AccountMixin.NORMAL: - return - if self.dialog.ui.radioButtonUnregister.isChecked() and isinstance(acct, GatewayAccount): - acct.unregister() - BMConfigParser().remove_option(addressAtCurrentRow, 'gateway') - BMConfigParser().save() - self.statusBar().showMessage(_translate( - "MainWindow", "Sending email gateway unregistration request"), 10000) - elif self.dialog.ui.radioButtonStatus.isChecked() and isinstance(acct, GatewayAccount): - acct.status() - self.statusBar().showMessage(_translate( - "MainWindow", "Sending email gateway status request"), 10000) - elif self.dialog.ui.radioButtonSettings.isChecked() and isinstance(acct, GatewayAccount): - acct.settings() - listOfAddressesInComboBoxSendFrom = [str(self.ui.comboBoxSendFrom.itemData(i).toPyObject()) for i in range(self.ui.comboBoxSendFrom.count())] - if acct.fromAddress in listOfAddressesInComboBoxSendFrom: - currentIndex = listOfAddressesInComboBoxSendFrom.index(acct.fromAddress) - self.ui.comboBoxSendFrom.setCurrentIndex(currentIndex) - else: - self.ui.comboBoxSendFrom.setCurrentIndex(0) - self.ui.lineEditTo.setText(acct.toAddress) - self.ui.lineEditSubject.setText(acct.subject) - self.ui.textEditMessage.setText(acct.message) - self.ui.tabWidgetSend.setCurrentIndex( - self.ui.tabWidgetSend.indexOf(self.ui.sendDirect) - ) - self.ui.tabWidget.setCurrentIndex( - self.ui.tabWidget.indexOf(self.ui.send) - ) - self.ui.textEditMessage.setFocus() - elif self.dialog.ui.radioButtonRegister.isChecked(): - email = str(self.dialog.ui.lineEditEmail.text().toUtf8()) - acct = MailchuckAccount(addressAtCurrentRow) - acct.register(email) - BMConfigParser().set(addressAtCurrentRow, 'label', email) - BMConfigParser().set(addressAtCurrentRow, 'gateway', 'mailchuck') - BMConfigParser().save() - self.statusBar().showMessage(_translate( - "MainWindow", "Sending email gateway registration request"), 10000) + acct = dialog.exec_() + + # Only settings ramain here + if acct: + acct.settings() + for i in range(self.ui.comboBoxSendFrom.count()): + if str(self.ui.comboBoxSendFrom.itemData(i).toPyObject()) \ + == acct.fromAddress: + self.ui.comboBoxSendFrom.setCurrentIndex(i) + break else: - pass - #print "well nothing" -# shared.writeKeysFile() -# self.rerenderInboxToLabels() + self.ui.comboBoxSendFrom.setCurrentIndex(0) + + self.ui.lineEditTo.setText(acct.toAddress) + self.ui.lineEditSubject.setText(acct.subject) + self.ui.textEditMessage.setText(acct.message) + self.ui.tabWidgetSend.setCurrentIndex( + self.ui.tabWidgetSend.indexOf(self.ui.sendDirect) + ) + self.ui.tabWidget.setCurrentIndex( + self.ui.tabWidget.indexOf(self.ui.send) + ) + self.ui.textEditMessage.setFocus() def on_action_MarkAllRead(self): if QtGui.QMessageBox.question( @@ -4210,44 +4186,6 @@ class settingsDialog(QtGui.QDialog): self.parent.ui.pushButtonFetchNamecoinID.show() -class EmailGatewayDialog(QtGui.QDialog): - - def __init__(self, parent): - QtGui.QWidget.__init__(self, parent) - self.ui = Ui_EmailGatewayDialog() - self.ui.setupUi(self) - self.parent = parent - addressAtCurrentRow = parent.getCurrentAccount() - acct = accountClass(addressAtCurrentRow) - if isinstance(acct, GatewayAccount): - self.ui.radioButtonUnregister.setEnabled(True) - self.ui.radioButtonStatus.setEnabled(True) - self.ui.radioButtonStatus.setChecked(True) - self.ui.radioButtonSettings.setEnabled(True) - else: - self.ui.radioButtonStatus.setEnabled(False) - self.ui.radioButtonSettings.setEnabled(False) - self.ui.radioButtonUnregister.setEnabled(False) - label = BMConfigParser().get(addressAtCurrentRow, 'label') - if label.find("@mailchuck.com") > -1: - self.ui.lineEditEmail.setText(label) - - QtGui.QWidget.resize(self, QtGui.QWidget.sizeHint(self)) - - -class EmailGatewayRegistrationDialog(QtGui.QDialog): - - def __init__(self, parent, title, label): - QtGui.QWidget.__init__(self, parent) - self.ui = Ui_EmailGatewayRegistrationDialog() - self.ui.setupUi(self) - self.parent = parent - self.setWindowTitle(title) - self.ui.label.setText(label) - - QtGui.QWidget.resize(self, QtGui.QWidget.sizeHint(self)) - - class NewAddressDialog(QtGui.QDialog): def __init__(self, parent): diff --git a/src/bitmessageqt/dialogs.py b/src/bitmessageqt/dialogs.py index 58003f3a..c6fee734 100644 --- a/src/bitmessageqt/dialogs.py +++ b/src/bitmessageqt/dialogs.py @@ -1,5 +1,6 @@ from PyQt4 import QtCore, QtGui from addresses import decodeAddress, encodeVarint +from account import GatewayAccount, MailchuckAccount, AccountMixin, accountClass from tr import _translate from retranslateui import RetranslateMixin import widgets @@ -235,3 +236,79 @@ class SpecialAddressBehaviorDialog(QtGui.QDialog, RetranslateMixin): self.parent.rerenderComboBoxSendFromBroadcast() self.config.save() self.parent.rerenderMessagelistToLabels() + + +class EmailGatewayDialog(QtGui.QDialog, RetranslateMixin): + def __init__(self, parent, title=None, label=None, config=None): + super(EmailGatewayDialog, self).__init__(parent) + widgets.load('emailgateway.ui', self) + self.parent = parent + self.config = config + if title and label: + self.setWindowTitle(title) + self.label.setText(label) + self.radioButtonRegister.hide() + self.radioButtonStatus.hide() + self.radioButtonSettings.hide() + self.radioButtonUnregister.hide() + else: + address = parent.getCurrentAccount() + self.acct = accountClass(address) + try: + label = config.get(address, 'label') + except AttributeError: + pass + else: + if "@" in label: + self.lineEditEmail.setText(label) + if isinstance(self.acct, GatewayAccount): + self.radioButtonUnregister.setEnabled(True) + self.radioButtonStatus.setEnabled(True) + self.radioButtonStatus.setChecked(True) + self.radioButtonSettings.setEnabled(True) + self.lineEditEmail.setEnabled(False) + else: + self.acct = MailchuckAccount(address) + self.lineEditEmail.setFocus() + QtGui.QWidget.resize(self, QtGui.QWidget.sizeHint(self)) + + def register(self, acct=None): + email = str(self.lineEditEmail.text().toUtf8()) + if acct is None: + acct = self.acct + acct.register(email) + self.config.set(acct.fromAddress, 'label', email) + self.config.set(acct.fromAddress, 'gateway', 'mailchuck') + self.config.save() + self.parent.statusBar().showMessage(_translate( + "MainWindow", + "Sending email gateway registration request" + ), 10000) + + def accept(self): + self.hide() + # no chans / mailinglists + if self.acct.type != AccountMixin.NORMAL: + return + + if not isinstance(self.acct, GatewayAccount): + return + + if self.radioButtonRegister.isChecked(): + self.register() + elif self.radioButtonUnregister.isChecked(): + self.acct.unregister() + self.config.remove_option(self.acct.fromAddress, 'gateway') + self.config.save() + self.parent.statusBar().showMessage(_translate( + "MainWindow", + "Sending email gateway unregistration request" + ), 10000) + elif self.radioButtonStatus.isChecked(): + self.acct.status() + self.parent.statusBar().showMessage(_translate( + "MainWindow", + "Sending email gateway status request" + ), 10000) + elif self.radioButtonSettings.isChecked(): + return self.acct diff --git a/src/bitmessageqt/emailgateway.py b/src/bitmessageqt/emailgateway.py deleted file mode 100644 index 54ca4529..00000000 --- a/src/bitmessageqt/emailgateway.py +++ /dev/null @@ -1,103 +0,0 @@ -# -*- coding: utf-8 -*- - -# Form implementation generated from reading ui file 'emailgateway.ui' -# -# Created: Fri Apr 26 17:43:31 2013 -# by: PyQt4 UI code generator 4.9.4 -# -# WARNING! All changes made in this file will be lost! - -from PyQt4 import QtCore, QtGui - -try: - _fromUtf8 = QtCore.QString.fromUtf8 -except AttributeError: - _fromUtf8 = lambda s: s - -class Ui_EmailGatewayDialog(object): - def setupUi(self, EmailGatewayDialog): - EmailGatewayDialog.setObjectName(_fromUtf8("EmailGatewayDialog")) - EmailGatewayDialog.resize(386, 172) - self.gridLayout = QtGui.QGridLayout(EmailGatewayDialog) - self.gridLayout.setObjectName(_fromUtf8("gridLayout")) - self.radioButtonRegister = QtGui.QRadioButton(EmailGatewayDialog) - self.radioButtonRegister.setChecked(True) - self.radioButtonRegister.setObjectName(_fromUtf8("radioButtonRegister")) - self.gridLayout.addWidget(self.radioButtonRegister, 1, 0, 1, 1) - self.radioButtonStatus = QtGui.QRadioButton(EmailGatewayDialog) - self.radioButtonStatus.setObjectName(_fromUtf8("radioButtonStatus")) - self.gridLayout.addWidget(self.radioButtonStatus, 4, 0, 1, 1) - self.radioButtonSettings = QtGui.QRadioButton(EmailGatewayDialog) - self.radioButtonSettings.setObjectName(_fromUtf8("radioButtonSettings")) - self.gridLayout.addWidget(self.radioButtonSettings, 5, 0, 1, 1) - self.radioButtonUnregister = QtGui.QRadioButton(EmailGatewayDialog) - self.radioButtonUnregister.setObjectName(_fromUtf8("radioButtonUnregister")) - self.gridLayout.addWidget(self.radioButtonUnregister, 6, 0, 1, 1) - self.label = QtGui.QLabel(EmailGatewayDialog) - self.label.setWordWrap(True) - self.label.setObjectName(_fromUtf8("label")) - self.gridLayout.addWidget(self.label, 0, 0, 1, 1) - self.label_2 = QtGui.QLabel(EmailGatewayDialog) - self.label_2.setObjectName(_fromUtf8("label_2")) - self.gridLayout.addWidget(self.label_2, 2, 0, 1, 1) - self.lineEditEmail = QtGui.QLineEdit(EmailGatewayDialog) - self.lineEditEmail.setEnabled(True) - self.lineEditEmail.setObjectName(_fromUtf8("lineEditEmail")) - self.gridLayout.addWidget(self.lineEditEmail, 3, 0, 1, 1) - self.buttonBox = QtGui.QDialogButtonBox(EmailGatewayDialog) - self.buttonBox.setMinimumSize(QtCore.QSize(368, 0)) - self.buttonBox.setOrientation(QtCore.Qt.Horizontal) - self.buttonBox.setStandardButtons(QtGui.QDialogButtonBox.Cancel|QtGui.QDialogButtonBox.Ok) - self.buttonBox.setObjectName(_fromUtf8("buttonBox")) - self.gridLayout.addWidget(self.buttonBox, 7, 0, 1, 1) - - self.retranslateUi(EmailGatewayDialog) - QtCore.QObject.connect(self.buttonBox, QtCore.SIGNAL(_fromUtf8("accepted()")), EmailGatewayDialog.accept) - QtCore.QObject.connect(self.buttonBox, QtCore.SIGNAL(_fromUtf8("rejected()")), EmailGatewayDialog.reject) - QtCore.QObject.connect(self.radioButtonRegister, QtCore.SIGNAL(_fromUtf8("clicked(bool)")), self.lineEditEmail.setEnabled) - QtCore.QObject.connect(self.radioButtonStatus, QtCore.SIGNAL(_fromUtf8("clicked(bool)")), self.lineEditEmail.setDisabled) - QtCore.QObject.connect(self.radioButtonSettings, QtCore.SIGNAL(_fromUtf8("clicked(bool)")), self.lineEditEmail.setDisabled) - QtCore.QObject.connect(self.radioButtonUnregister, QtCore.SIGNAL(_fromUtf8("clicked(bool)")), self.lineEditEmail.setDisabled) - QtCore.QMetaObject.connectSlotsByName(EmailGatewayDialog) - EmailGatewayDialog.setTabOrder(self.radioButtonRegister, self.lineEditEmail) - EmailGatewayDialog.setTabOrder(self.lineEditEmail, self.radioButtonUnregister) - EmailGatewayDialog.setTabOrder(self.radioButtonUnregister, self.buttonBox) - - def retranslateUi(self, EmailGatewayDialog): - EmailGatewayDialog.setWindowTitle(QtGui.QApplication.translate("EmailGatewayDialog", "Email gateway", None, QtGui.QApplication.UnicodeUTF8)) - self.radioButtonRegister.setText(QtGui.QApplication.translate("EmailGatewayDialog", "Register on email gateway", None, QtGui.QApplication.UnicodeUTF8)) - self.radioButtonStatus.setText(QtGui.QApplication.translate("EmailGatewayDialog", "Account status at email gateway", None, QtGui.QApplication.UnicodeUTF8)) - self.radioButtonSettings.setText(QtGui.QApplication.translate("EmailGatewayDialog", "Change account settings at email gateway", None, QtGui.QApplication.UnicodeUTF8)) - self.radioButtonUnregister.setText(QtGui.QApplication.translate("EmailGatewayDialog", "Unregister from email gateway", None, QtGui.QApplication.UnicodeUTF8)) - self.label.setText(QtGui.QApplication.translate("EmailGatewayDialog", "Email gateway allows you to communicate with email users. Currently, only the Mailchuck email gateway (@mailchuck.com) is available.", None, QtGui.QApplication.UnicodeUTF8)) - self.label_2.setText(QtGui.QApplication.translate("EmailGatewayDialog", "Desired email address (including @mailchuck.com):", None, QtGui.QApplication.UnicodeUTF8)) - - -class Ui_EmailGatewayRegistrationDialog(object): - def setupUi(self, EmailGatewayRegistrationDialog): - EmailGatewayRegistrationDialog.setObjectName(_fromUtf8("EmailGatewayRegistrationDialog")) - EmailGatewayRegistrationDialog.resize(386, 172) - self.gridLayout = QtGui.QGridLayout(EmailGatewayRegistrationDialog) - self.gridLayout.setObjectName(_fromUtf8("gridLayout")) - self.label = QtGui.QLabel(EmailGatewayRegistrationDialog) - self.label.setWordWrap(True) - self.label.setObjectName(_fromUtf8("label")) - self.gridLayout.addWidget(self.label, 0, 0, 1, 1) - self.lineEditEmail = QtGui.QLineEdit(EmailGatewayRegistrationDialog) - self.lineEditEmail.setObjectName(_fromUtf8("lineEditEmail")) - self.gridLayout.addWidget(self.lineEditEmail, 1, 0, 1, 1) - self.buttonBox = QtGui.QDialogButtonBox(EmailGatewayRegistrationDialog) - self.buttonBox.setMinimumSize(QtCore.QSize(368, 0)) - self.buttonBox.setOrientation(QtCore.Qt.Horizontal) - self.buttonBox.setStandardButtons(QtGui.QDialogButtonBox.Cancel|QtGui.QDialogButtonBox.Ok) - self.buttonBox.setObjectName(_fromUtf8("buttonBox")) - self.gridLayout.addWidget(self.buttonBox, 7, 0, 1, 1) - - self.retranslateUi(EmailGatewayRegistrationDialog) - QtCore.QObject.connect(self.buttonBox, QtCore.SIGNAL(_fromUtf8("accepted()")), EmailGatewayRegistrationDialog.accept) - QtCore.QObject.connect(self.buttonBox, QtCore.SIGNAL(_fromUtf8("rejected()")), EmailGatewayRegistrationDialog.reject) - QtCore.QMetaObject.connectSlotsByName(EmailGatewayRegistrationDialog) - - def retranslateUi(self, EmailGatewayRegistrationDialog): - EmailGatewayRegistrationDialog.setWindowTitle(QtGui.QApplication.translate("EmailGatewayRegistrationDialog", "Email gateway registration", None, QtGui.QApplication.UnicodeUTF8)) - self.label.setText(QtGui.QApplication.translate("EmailGatewayRegistrationDialog", "Email gateway allows you to communicate with email users. Currently, only the Mailchuck email gateway (@mailchuck.com) is available.\nPlease type the desired email address (including @mailchuck.com) below:", None, QtGui.QApplication.UnicodeUTF8)) diff --git a/src/bitmessageqt/emailgateway.ui b/src/bitmessageqt/emailgateway.ui index 927df46a..77a66dec 100644 --- a/src/bitmessageqt/emailgateway.ui +++ b/src/bitmessageqt/emailgateway.ui @@ -7,20 +7,20 @@ 0 0 386 - 172 + 240 Email gateway - + true - Desired email address (including @mailchuck.com) + Desired email address (including @mailchuck.com): @@ -50,28 +50,60 @@ - - + + true @mailchuck.com + + 0 + - Email gateway alows you to communicate with email users. Currently, only the Mailchuck email gateway (@mailchuck.com) is available. + Email gateway allows you to communicate with email users. Currently, only the Mailchuck email gateway (@mailchuck.com) is available. true + + + + false + + + Account status at email gateway + + + false + + + + + + + false + + + Change account settings at email gateway + + + false + + + + + false + Unregister from email gateway @@ -84,7 +116,10 @@ radioButtonRegister - lineEditEmailAddress + lineEditEmail + radioButtonStatus + radioButtonSettings + radioButtonUnregister buttonBox @@ -124,7 +159,7 @@ radioButtonRegister clicked(bool) - lineEditEmailAddress + lineEditEmail setEnabled(bool) @@ -140,7 +175,7 @@ radioButtonUnregister clicked(bool) - lineEditEmailAddress + lineEditEmail setDisabled(bool) @@ -153,5 +188,37 @@ + + radioButtonStatus + clicked(bool) + lineEditEmail + setDisabled(bool) + + + 20 + 20 + + + 20 + 20 + + + + + radioButtonSettings + clicked(bool) + lineEditEmail + setDisabled(bool) + + + 20 + 20 + + + 20 + 20 + + + From 35a11c271a2e9e6937a7dd8d34b377a5bf314383 Mon Sep 17 00:00:00 2001 From: Dmitri Bogomolov <4glitch@gmail.com> Date: Fri, 19 Jan 2018 18:30:35 +0200 Subject: [PATCH 343/407] Moved NewAddressDialog into dialogs module --- src/bitmessageqt/__init__.py | 99 +++++++------- src/bitmessageqt/dialogs.py | 20 ++- src/bitmessageqt/newaddressdialog.py | 193 --------------------------- 3 files changed, 64 insertions(+), 248 deletions(-) delete mode 100644 src/bitmessageqt/newaddressdialog.py diff --git a/src/bitmessageqt/__init__.py b/src/bitmessageqt/__init__.py index 197d9d21..a44fd86a 100644 --- a/src/bitmessageqt/__init__.py +++ b/src/bitmessageqt/__init__.py @@ -22,8 +22,6 @@ from bitmessageui import * from bmconfigparser import BMConfigParser import defaults from namecoin import namecoinConnection -from newaddressdialog import * -from newaddresswizard import * from messageview import MessageView from migrationwizard import * from foldertree import * @@ -2554,41 +2552,53 @@ class MyForm(settingsmixin.SMainWindow): addressAtCurrentRow, self.getCurrentFolder(), None, 0) def click_NewAddressDialog(self): - addresses = [] - for addressInKeysFile in getSortedAccounts(): - addresses.append(addressInKeysFile) -# self.dialog = Ui_NewAddressWizard(addresses) -# self.dialog.exec_() -# print "Name: " + self.dialog.field("name").toString() -# print "Email: " + self.dialog.field("email").toString() -# return - self.dialog = NewAddressDialog(self) + dialog = dialogs.NewAddressDialog(self) # For Modal dialogs - if self.dialog.exec_(): - # self.dialog.ui.buttonBox.enabled = False - if self.dialog.ui.radioButtonRandomAddress.isChecked(): - if self.dialog.ui.radioButtonMostAvailable.isChecked(): - streamNumberForAddress = 1 - else: - # User selected 'Use the same stream as an existing - # address.' - streamNumberForAddress = decodeAddress( - self.dialog.ui.comboBoxExisting.currentText())[2] - queues.addressGeneratorQueue.put(('createRandomAddress', 4, streamNumberForAddress, str( - self.dialog.ui.newaddresslabel.text().toUtf8()), 1, "", self.dialog.ui.checkBoxEighteenByteRipe.isChecked())) - else: - if self.dialog.ui.lineEditPassphrase.text() != self.dialog.ui.lineEditPassphraseAgain.text(): - QMessageBox.about(self, _translate("MainWindow", "Passphrase mismatch"), _translate( - "MainWindow", "The passphrase you entered twice doesn\'t match. Try again.")) - elif self.dialog.ui.lineEditPassphrase.text() == "": - QMessageBox.about(self, _translate( - "MainWindow", "Choose a passphrase"), _translate("MainWindow", "You really do need a passphrase.")) - else: - streamNumberForAddress = 1 # this will eventually have to be replaced by logic to determine the most available stream number. - queues.addressGeneratorQueue.put(('createDeterministicAddresses', 4, streamNumberForAddress, "unused deterministic address", self.dialog.ui.spinBoxNumberOfAddressesToMake.value( - ), self.dialog.ui.lineEditPassphrase.text().toUtf8(), self.dialog.ui.checkBoxEighteenByteRipe.isChecked())) - else: + if not dialog.exec_(): logger.debug('new address dialog box rejected') + return + + # dialog.buttonBox.enabled = False + if dialog.radioButtonRandomAddress.isChecked(): + if dialog.radioButtonMostAvailable.isChecked(): + streamNumberForAddress = 1 + else: + # User selected 'Use the same stream as an existing + # address.' + streamNumberForAddress = decodeAddress( + dialog.comboBoxExisting.currentText())[2] + queues.addressGeneratorQueue.put(( + 'createRandomAddress', 4, streamNumberForAddress, + str(dialog.newaddresslabel.text().toUtf8()), 1, "", + dialog.checkBoxEighteenByteRipe.isChecked() + )) + else: + if dialog.lineEditPassphrase.text() != \ + dialog.lineEditPassphraseAgain.text(): + QMessageBox.about( + self, _translate("MainWindow", "Passphrase mismatch"), + _translate( + "MainWindow", + "The passphrase you entered twice doesn\'t" + " match. Try again.") + ) + elif dialog.lineEditPassphrase.text() == "": + QMessageBox.about( + self, _translate("MainWindow", "Choose a passphrase"), + _translate( + "MainWindow", "You really do need a passphrase.") + ) + else: + # this will eventually have to be replaced by logic + # to determine the most available stream number. + streamNumberForAddress = 1 + queues.addressGeneratorQueue.put(( + 'createDeterministicAddresses', 4, streamNumberForAddress, + "unused deterministic address", + dialog.spinBoxNumberOfAddressesToMake.value(), + dialog.lineEditPassphrase.text().toUtf8(), + dialog.checkBoxEighteenByteRipe.isChecked() + )) def network_switch(self): dontconnect_option = not BMConfigParser().safeGetBoolean( @@ -4186,25 +4196,6 @@ class settingsDialog(QtGui.QDialog): self.parent.ui.pushButtonFetchNamecoinID.show() -class NewAddressDialog(QtGui.QDialog): - - def __init__(self, parent): - QtGui.QWidget.__init__(self, parent) - self.ui = Ui_NewAddressDialog() - self.ui.setupUi(self) - self.parent = parent - row = 1 - # Let's fill out the 'existing address' combo box with addresses from - # the 'Your Identities' tab. - for addressInKeysFile in getSortedAccounts(): - self.ui.radioButtonExisting.click() - self.ui.comboBoxExisting.addItem( - addressInKeysFile) - row += 1 - self.ui.groupBoxDeterministic.setHidden(True) - QtGui.QWidget.resize(self, QtGui.QWidget.sizeHint(self)) - - # In order for the time columns on the Inbox and Sent tabs to be sorted # correctly (rather than alphabetically), we need to overload the < # operator and use this class instead of QTableWidgetItem. diff --git a/src/bitmessageqt/dialogs.py b/src/bitmessageqt/dialogs.py index c6fee734..f7ac5497 100644 --- a/src/bitmessageqt/dialogs.py +++ b/src/bitmessageqt/dialogs.py @@ -1,6 +1,9 @@ from PyQt4 import QtCore, QtGui from addresses import decodeAddress, encodeVarint -from account import GatewayAccount, MailchuckAccount, AccountMixin, accountClass +from account import ( + GatewayAccount, MailchuckAccount, AccountMixin, accountClass, + getSortedAccounts +) from tr import _translate from retranslateui import RetranslateMixin import widgets @@ -70,6 +73,21 @@ class AddAddressDialog(QtGui.QDialog, RetranslateMixin, AddressCheckMixin): AddressCheckMixin.__init__(self) +class NewAddressDialog(QtGui.QDialog, RetranslateMixin): + + def __init__(self, parent=None): + super(NewAddressDialog, self).__init__(parent) + widgets.load('newaddressdialog.ui', self) + + # Let's fill out the 'existing address' combo box with addresses + # from the 'Your Identities' tab. + for address in getSortedAccounts(): + self.radioButtonExisting.click() + self.comboBoxExisting.addItem(address) + self.groupBoxDeterministic.setHidden(True) + QtGui.QWidget.resize(self, QtGui.QWidget.sizeHint(self)) + + class NewSubscriptionDialog( QtGui.QDialog, RetranslateMixin, AddressCheckMixin): diff --git a/src/bitmessageqt/newaddressdialog.py b/src/bitmessageqt/newaddressdialog.py deleted file mode 100644 index afe6fa2d..00000000 --- a/src/bitmessageqt/newaddressdialog.py +++ /dev/null @@ -1,193 +0,0 @@ -# -*- coding: utf-8 -*- - -# Form implementation generated from reading ui file 'newaddressdialog.ui' -# -# Created: Sun Sep 15 23:53:31 2013 -# by: PyQt4 UI code generator 4.10.2 -# -# WARNING! All changes made in this file will be lost! - -from PyQt4 import QtCore, QtGui - -try: - _fromUtf8 = QtCore.QString.fromUtf8 -except AttributeError: - def _fromUtf8(s): - return s - -try: - _encoding = QtGui.QApplication.UnicodeUTF8 - def _translate(context, text, disambig): - return QtGui.QApplication.translate(context, text, disambig, _encoding) -except AttributeError: - def _translate(context, text, disambig): - return QtGui.QApplication.translate(context, text, disambig) - -class Ui_NewAddressDialog(object): - def setupUi(self, NewAddressDialog): - NewAddressDialog.setObjectName(_fromUtf8("NewAddressDialog")) - NewAddressDialog.resize(723, 704) - self.formLayout = QtGui.QFormLayout(NewAddressDialog) - self.formLayout.setFieldGrowthPolicy(QtGui.QFormLayout.AllNonFixedFieldsGrow) - self.formLayout.setObjectName(_fromUtf8("formLayout")) - self.label = QtGui.QLabel(NewAddressDialog) - self.label.setAlignment(QtCore.Qt.AlignBottom|QtCore.Qt.AlignLeading|QtCore.Qt.AlignLeft) - self.label.setWordWrap(True) - self.label.setObjectName(_fromUtf8("label")) - self.formLayout.setWidget(0, QtGui.QFormLayout.SpanningRole, self.label) - self.label_5 = QtGui.QLabel(NewAddressDialog) - self.label_5.setWordWrap(True) - self.label_5.setObjectName(_fromUtf8("label_5")) - self.formLayout.setWidget(2, QtGui.QFormLayout.SpanningRole, self.label_5) - self.line = QtGui.QFrame(NewAddressDialog) - sizePolicy = QtGui.QSizePolicy(QtGui.QSizePolicy.Preferred, QtGui.QSizePolicy.Fixed) - sizePolicy.setHorizontalStretch(0) - sizePolicy.setVerticalStretch(0) - sizePolicy.setHeightForWidth(self.line.sizePolicy().hasHeightForWidth()) - self.line.setSizePolicy(sizePolicy) - self.line.setMinimumSize(QtCore.QSize(100, 2)) - self.line.setFrameShape(QtGui.QFrame.HLine) - self.line.setFrameShadow(QtGui.QFrame.Sunken) - self.line.setObjectName(_fromUtf8("line")) - self.formLayout.setWidget(4, QtGui.QFormLayout.SpanningRole, self.line) - self.radioButtonRandomAddress = QtGui.QRadioButton(NewAddressDialog) - self.radioButtonRandomAddress.setChecked(True) - self.radioButtonRandomAddress.setObjectName(_fromUtf8("radioButtonRandomAddress")) - self.buttonGroup = QtGui.QButtonGroup(NewAddressDialog) - self.buttonGroup.setObjectName(_fromUtf8("buttonGroup")) - self.buttonGroup.addButton(self.radioButtonRandomAddress) - self.formLayout.setWidget(5, QtGui.QFormLayout.SpanningRole, self.radioButtonRandomAddress) - self.radioButtonDeterministicAddress = QtGui.QRadioButton(NewAddressDialog) - self.radioButtonDeterministicAddress.setObjectName(_fromUtf8("radioButtonDeterministicAddress")) - self.buttonGroup.addButton(self.radioButtonDeterministicAddress) - self.formLayout.setWidget(6, QtGui.QFormLayout.LabelRole, self.radioButtonDeterministicAddress) - self.checkBoxEighteenByteRipe = QtGui.QCheckBox(NewAddressDialog) - self.checkBoxEighteenByteRipe.setObjectName(_fromUtf8("checkBoxEighteenByteRipe")) - self.formLayout.setWidget(9, QtGui.QFormLayout.SpanningRole, self.checkBoxEighteenByteRipe) - self.groupBoxDeterministic = QtGui.QGroupBox(NewAddressDialog) - self.groupBoxDeterministic.setObjectName(_fromUtf8("groupBoxDeterministic")) - self.gridLayout = QtGui.QGridLayout(self.groupBoxDeterministic) - self.gridLayout.setObjectName(_fromUtf8("gridLayout")) - self.label_9 = QtGui.QLabel(self.groupBoxDeterministic) - self.label_9.setObjectName(_fromUtf8("label_9")) - self.gridLayout.addWidget(self.label_9, 6, 0, 1, 1) - self.label_8 = QtGui.QLabel(self.groupBoxDeterministic) - self.label_8.setObjectName(_fromUtf8("label_8")) - self.gridLayout.addWidget(self.label_8, 5, 0, 1, 3) - self.spinBoxNumberOfAddressesToMake = QtGui.QSpinBox(self.groupBoxDeterministic) - self.spinBoxNumberOfAddressesToMake.setMinimum(1) - self.spinBoxNumberOfAddressesToMake.setProperty("value", 8) - self.spinBoxNumberOfAddressesToMake.setObjectName(_fromUtf8("spinBoxNumberOfAddressesToMake")) - self.gridLayout.addWidget(self.spinBoxNumberOfAddressesToMake, 4, 3, 1, 1) - self.label_6 = QtGui.QLabel(self.groupBoxDeterministic) - self.label_6.setObjectName(_fromUtf8("label_6")) - self.gridLayout.addWidget(self.label_6, 0, 0, 1, 1) - self.label_11 = QtGui.QLabel(self.groupBoxDeterministic) - self.label_11.setObjectName(_fromUtf8("label_11")) - self.gridLayout.addWidget(self.label_11, 4, 0, 1, 3) - spacerItem = QtGui.QSpacerItem(73, 20, QtGui.QSizePolicy.Expanding, QtGui.QSizePolicy.Minimum) - self.gridLayout.addItem(spacerItem, 6, 1, 1, 1) - self.label_10 = QtGui.QLabel(self.groupBoxDeterministic) - self.label_10.setObjectName(_fromUtf8("label_10")) - self.gridLayout.addWidget(self.label_10, 6, 2, 1, 1) - spacerItem1 = QtGui.QSpacerItem(42, 20, QtGui.QSizePolicy.Expanding, QtGui.QSizePolicy.Minimum) - self.gridLayout.addItem(spacerItem1, 6, 3, 1, 1) - self.label_7 = QtGui.QLabel(self.groupBoxDeterministic) - self.label_7.setObjectName(_fromUtf8("label_7")) - self.gridLayout.addWidget(self.label_7, 2, 0, 1, 1) - self.lineEditPassphraseAgain = QtGui.QLineEdit(self.groupBoxDeterministic) - self.lineEditPassphraseAgain.setEchoMode(QtGui.QLineEdit.Password) - self.lineEditPassphraseAgain.setObjectName(_fromUtf8("lineEditPassphraseAgain")) - self.gridLayout.addWidget(self.lineEditPassphraseAgain, 3, 0, 1, 4) - self.lineEditPassphrase = QtGui.QLineEdit(self.groupBoxDeterministic) - self.lineEditPassphrase.setInputMethodHints(QtCore.Qt.ImhHiddenText|QtCore.Qt.ImhNoAutoUppercase|QtCore.Qt.ImhNoPredictiveText) - self.lineEditPassphrase.setEchoMode(QtGui.QLineEdit.Password) - self.lineEditPassphrase.setObjectName(_fromUtf8("lineEditPassphrase")) - self.gridLayout.addWidget(self.lineEditPassphrase, 1, 0, 1, 4) - self.formLayout.setWidget(8, QtGui.QFormLayout.LabelRole, self.groupBoxDeterministic) - self.groupBox = QtGui.QGroupBox(NewAddressDialog) - self.groupBox.setObjectName(_fromUtf8("groupBox")) - self.gridLayout_2 = QtGui.QGridLayout(self.groupBox) - self.gridLayout_2.setObjectName(_fromUtf8("gridLayout_2")) - self.label_2 = QtGui.QLabel(self.groupBox) - self.label_2.setObjectName(_fromUtf8("label_2")) - self.gridLayout_2.addWidget(self.label_2, 0, 0, 1, 2) - self.newaddresslabel = QtGui.QLineEdit(self.groupBox) - self.newaddresslabel.setObjectName(_fromUtf8("newaddresslabel")) - self.gridLayout_2.addWidget(self.newaddresslabel, 1, 0, 1, 2) - self.radioButtonMostAvailable = QtGui.QRadioButton(self.groupBox) - self.radioButtonMostAvailable.setChecked(True) - self.radioButtonMostAvailable.setObjectName(_fromUtf8("radioButtonMostAvailable")) - self.gridLayout_2.addWidget(self.radioButtonMostAvailable, 2, 0, 1, 2) - self.label_3 = QtGui.QLabel(self.groupBox) - self.label_3.setAlignment(QtCore.Qt.AlignLeading|QtCore.Qt.AlignLeft|QtCore.Qt.AlignTop) - self.label_3.setObjectName(_fromUtf8("label_3")) - self.gridLayout_2.addWidget(self.label_3, 3, 1, 1, 1) - self.radioButtonExisting = QtGui.QRadioButton(self.groupBox) - self.radioButtonExisting.setChecked(False) - self.radioButtonExisting.setObjectName(_fromUtf8("radioButtonExisting")) - self.gridLayout_2.addWidget(self.radioButtonExisting, 4, 0, 1, 2) - self.label_4 = QtGui.QLabel(self.groupBox) - self.label_4.setAlignment(QtCore.Qt.AlignLeading|QtCore.Qt.AlignLeft|QtCore.Qt.AlignTop) - self.label_4.setObjectName(_fromUtf8("label_4")) - self.gridLayout_2.addWidget(self.label_4, 5, 1, 1, 1) - spacerItem2 = QtGui.QSpacerItem(13, 20, QtGui.QSizePolicy.Expanding, QtGui.QSizePolicy.Minimum) - self.gridLayout_2.addItem(spacerItem2, 6, 0, 1, 1) - self.comboBoxExisting = QtGui.QComboBox(self.groupBox) - self.comboBoxExisting.setEnabled(False) - self.comboBoxExisting.setEditable(True) - self.comboBoxExisting.setObjectName(_fromUtf8("comboBoxExisting")) - self.gridLayout_2.addWidget(self.comboBoxExisting, 6, 1, 1, 1) - self.formLayout.setWidget(7, QtGui.QFormLayout.LabelRole, self.groupBox) - self.buttonBox = QtGui.QDialogButtonBox(NewAddressDialog) - sizePolicy = QtGui.QSizePolicy(QtGui.QSizePolicy.MinimumExpanding, QtGui.QSizePolicy.MinimumExpanding) - sizePolicy.setHorizontalStretch(0) - sizePolicy.setVerticalStretch(0) - sizePolicy.setHeightForWidth(self.buttonBox.sizePolicy().hasHeightForWidth()) - self.buttonBox.setSizePolicy(sizePolicy) - self.buttonBox.setMinimumSize(QtCore.QSize(160, 0)) - self.buttonBox.setOrientation(QtCore.Qt.Horizontal) - self.buttonBox.setStandardButtons(QtGui.QDialogButtonBox.Cancel|QtGui.QDialogButtonBox.Ok) - self.buttonBox.setObjectName(_fromUtf8("buttonBox")) - self.formLayout.setWidget(10, QtGui.QFormLayout.SpanningRole, self.buttonBox) - - self.retranslateUi(NewAddressDialog) - QtCore.QObject.connect(self.buttonBox, QtCore.SIGNAL(_fromUtf8("accepted()")), NewAddressDialog.accept) - QtCore.QObject.connect(self.buttonBox, QtCore.SIGNAL(_fromUtf8("rejected()")), NewAddressDialog.reject) - QtCore.QObject.connect(self.radioButtonExisting, QtCore.SIGNAL(_fromUtf8("toggled(bool)")), self.comboBoxExisting.setEnabled) - QtCore.QObject.connect(self.radioButtonDeterministicAddress, QtCore.SIGNAL(_fromUtf8("toggled(bool)")), self.groupBoxDeterministic.setShown) - QtCore.QObject.connect(self.radioButtonRandomAddress, QtCore.SIGNAL(_fromUtf8("toggled(bool)")), self.groupBox.setShown) - QtCore.QMetaObject.connectSlotsByName(NewAddressDialog) - NewAddressDialog.setTabOrder(self.radioButtonRandomAddress, self.radioButtonDeterministicAddress) - NewAddressDialog.setTabOrder(self.radioButtonDeterministicAddress, self.newaddresslabel) - NewAddressDialog.setTabOrder(self.newaddresslabel, self.radioButtonMostAvailable) - NewAddressDialog.setTabOrder(self.radioButtonMostAvailable, self.radioButtonExisting) - NewAddressDialog.setTabOrder(self.radioButtonExisting, self.comboBoxExisting) - NewAddressDialog.setTabOrder(self.comboBoxExisting, self.lineEditPassphrase) - NewAddressDialog.setTabOrder(self.lineEditPassphrase, self.lineEditPassphraseAgain) - NewAddressDialog.setTabOrder(self.lineEditPassphraseAgain, self.spinBoxNumberOfAddressesToMake) - NewAddressDialog.setTabOrder(self.spinBoxNumberOfAddressesToMake, self.checkBoxEighteenByteRipe) - NewAddressDialog.setTabOrder(self.checkBoxEighteenByteRipe, self.buttonBox) - - def retranslateUi(self, NewAddressDialog): - NewAddressDialog.setWindowTitle(_translate("NewAddressDialog", "Create new Address", None)) - self.label.setText(_translate("NewAddressDialog", "Here you may generate as many addresses as you like. Indeed, creating and abandoning addresses is encouraged. You may generate addresses by using either random numbers or by using a passphrase. If you use a passphrase, the address is called a \"deterministic\" address.\n" -"The \'Random Number\' option is selected by default but deterministic addresses have several pros and cons:", None)) - self.label_5.setText(_translate("NewAddressDialog", "

Pros:
You can recreate your addresses on any computer from memory.
You need-not worry about backing up your keys.dat file as long as you can remember your passphrase.
Cons:
You must remember (or write down) your passphrase if you expect to be able to recreate your keys if they are lost.
You must remember the address version number and the stream number along with your passphrase.
If you choose a weak passphrase and someone on the Internet can brute-force it, they can read your messages and send messages as you.

", None)) - self.radioButtonRandomAddress.setText(_translate("NewAddressDialog", "Use a random number generator to make an address", None)) - self.radioButtonDeterministicAddress.setText(_translate("NewAddressDialog", "Use a passphrase to make addresses", None)) - self.checkBoxEighteenByteRipe.setText(_translate("NewAddressDialog", "Spend several minutes of extra computing time to make the address(es) 1 or 2 characters shorter", None)) - self.groupBoxDeterministic.setTitle(_translate("NewAddressDialog", "Make deterministic addresses", None)) - self.label_9.setText(_translate("NewAddressDialog", "Address version number: 4", None)) - self.label_8.setText(_translate("NewAddressDialog", "In addition to your passphrase, you must remember these numbers:", None)) - self.label_6.setText(_translate("NewAddressDialog", "Passphrase", None)) - self.label_11.setText(_translate("NewAddressDialog", "Number of addresses to make based on your passphrase:", None)) - self.label_10.setText(_translate("NewAddressDialog", "Stream number: 1", None)) - self.label_7.setText(_translate("NewAddressDialog", "Retype passphrase", None)) - self.groupBox.setTitle(_translate("NewAddressDialog", "Randomly generate address", None)) - self.label_2.setText(_translate("NewAddressDialog", "Label (not shown to anyone except you)", None)) - self.radioButtonMostAvailable.setText(_translate("NewAddressDialog", "Use the most available stream", None)) - self.label_3.setText(_translate("NewAddressDialog", " (best if this is the first of many addresses you will create)", None)) - self.radioButtonExisting.setText(_translate("NewAddressDialog", "Use the same stream as an existing address", None)) - self.label_4.setText(_translate("NewAddressDialog", "(saves you some bandwidth and processing power)", None)) - From cd88d063a1899bdc3212d5d1ff57b99452000943 Mon Sep 17 00:00:00 2001 From: Dmitri Bogomolov <4glitch@gmail.com> Date: Fri, 19 Jan 2018 18:38:15 +0200 Subject: [PATCH 344/407] Imported NewChanDialog into dialogs module --- src/bitmessageqt/__init__.py | 3 +-- src/bitmessageqt/dialogs.py | 5 +++++ 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/src/bitmessageqt/__init__.py b/src/bitmessageqt/__init__.py index a44fd86a..34ab49b5 100644 --- a/src/bitmessageqt/__init__.py +++ b/src/bitmessageqt/__init__.py @@ -25,7 +25,6 @@ from namecoin import namecoinConnection from messageview import MessageView from migrationwizard import * from foldertree import * -from newchandialog import * from safehtmlparser import * from settings import * import settingsmixin @@ -1488,7 +1487,7 @@ class MyForm(settingsmixin.SMainWindow): # opens 'join chan' dialog def click_actionJoinChan(self): - NewChanDialog(self) + dialogs.NewChanDialog(self) def showConnectDialog(self): dialog = dialogs.ConnectDialog(self) diff --git a/src/bitmessageqt/dialogs.py b/src/bitmessageqt/dialogs.py index f7ac5497..c7cd5bd4 100644 --- a/src/bitmessageqt/dialogs.py +++ b/src/bitmessageqt/dialogs.py @@ -8,12 +8,17 @@ from tr import _translate from retranslateui import RetranslateMixin import widgets +from newchandialog import NewChanDialog + import hashlib import paths from inventory import Inventory from version import softwareVersion +__all__ = [NewChanDialog] + + class AddressCheckMixin(object): def __init__(self): From 25876ded2bb1d2689db892a417cf503b1fd1278a Mon Sep 17 00:00:00 2001 From: Dmitri Bogomolov <4glitch@gmail.com> Date: Fri, 19 Jan 2018 18:42:44 +0200 Subject: [PATCH 345/407] Removed unused import of inventory.Inventory --- src/bitmessageqt/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/bitmessageqt/__init__.py b/src/bitmessageqt/__init__.py index 34ab49b5..54a98b14 100644 --- a/src/bitmessageqt/__init__.py +++ b/src/bitmessageqt/__init__.py @@ -50,7 +50,7 @@ from account import * import dialogs from helper_generic import powQueueSize from inventory import ( - Inventory, PendingDownloadQueue, PendingUpload, + PendingDownloadQueue, PendingUpload, PendingUploadDeadlineException) from uisignaler import UISignaler import knownnodes From 641db73614f86f7fad7e015801a7770e661368db Mon Sep 17 00:00:00 2001 From: Dmitri Bogomolov <4glitch@gmail.com> Date: Mon, 22 Jan 2018 15:19:02 +0200 Subject: [PATCH 346/407] Removed unused _encoding and _transalate --- src/bitmessageqt/__init__.py | 12 +----------- 1 file changed, 1 insertion(+), 11 deletions(-) diff --git a/src/bitmessageqt/__init__.py b/src/bitmessageqt/__init__.py index 54a98b14..70b84523 100644 --- a/src/bitmessageqt/__init__.py +++ b/src/bitmessageqt/__init__.py @@ -11,11 +11,7 @@ except Exception as err: import sys sys.exit() -try: - _encoding = QtGui.QApplication.UnicodeUTF8 -except AttributeError: - logger.exception('QtGui.QApplication.UnicodeUTF8 error', exc_info=True) - +from tr import _translate from addresses import * import shared from bitmessageui import * @@ -70,12 +66,6 @@ except ImportError: get_plugins = False -def _translate(context, text, disambiguation = None, encoding = None, number = None): - if number is None: - return QtGui.QApplication.translate(context, text) - else: - return QtGui.QApplication.translate(context, text, None, QtCore.QCoreApplication.CodecForTr, number) - def change_translation(newlocale): global qmytranslator, qsystranslator try: From c699f6d872cd97e2b21de2808fa345e03a463038 Mon Sep 17 00:00:00 2001 From: Dmitri Bogomolov <4glitch@gmail.com> Date: Mon, 22 Jan 2018 18:52:28 +0200 Subject: [PATCH 347/407] Got rid of wildcard imports in bitmessageqt.__init__ --- src/bitmessageqt/__init__.py | 300 +++++++++++++++++++++-------------- 1 file changed, 179 insertions(+), 121 deletions(-) diff --git a/src/bitmessageqt/__init__.py b/src/bitmessageqt/__init__.py index 70b84523..dfcb9685 100644 --- a/src/bitmessageqt/__init__.py +++ b/src/bitmessageqt/__init__.py @@ -1,32 +1,31 @@ from debug import logger +import sys try: from PyQt4 import QtCore, QtGui - from PyQt4.QtCore import * - from PyQt4.QtGui import * from PyQt4.QtNetwork import QLocalSocket, QLocalServer except Exception as err: logmsg = 'PyBitmessage requires PyQt unless you want to run it as a daemon and interact with it using the API. You can download it from http://www.riverbankcomputing.com/software/pyqt/download or by searching Google for \'PyQt Download\' (without quotes).' logger.critical(logmsg, exc_info=True) - import sys sys.exit() from tr import _translate -from addresses import * +from addresses import decodeAddress, addBMIfNotPresent import shared -from bitmessageui import * +from bitmessageui import Ui_MainWindow from bmconfigparser import BMConfigParser import defaults from namecoin import namecoinConnection from messageview import MessageView -from migrationwizard import * -from foldertree import * -from safehtmlparser import * -from settings import * +from migrationwizard import Ui_MigrationWizard +from foldertree import ( + AccountMixin, Ui_FolderWidget, Ui_AddressWidget, Ui_SubscriptionWidget, + MessageList_AddressWidget, MessageList_SubjectWidget, + Ui_AddressBookWidgetItemLabel, Ui_AddressBookWidgetItemAddress) +from settings import Ui_settingsDialog import settingsmixin import support import locale -import sys import time import os import hashlib @@ -42,7 +41,9 @@ import helper_search import l10n import openclpow from utils import str_broadcast_subscribers, avatarize -from account import * +from account import ( + getSortedAccounts, getSortedSubscriptions, accountClass, BMAccount, + GatewayAccount, MailchuckAccount, AccountColor) import dialogs from helper_generic import powQueueSize from inventory import ( @@ -378,7 +379,8 @@ class MyForm(settingsmixin.SMainWindow): # sort ascending when creating if treeWidget.topLevelItemCount() == 0: - treeWidget.header().setSortIndicator(0, Qt.AscendingOrder) + treeWidget.header().setSortIndicator( + 0, QtCore.Qt.AscendingOrder) # init dictionary db = getSortedSubscriptions(True) @@ -463,7 +465,8 @@ class MyForm(settingsmixin.SMainWindow): # sort ascending when creating if treeWidget.topLevelItemCount() == 0: - treeWidget.header().setSortIndicator(0, Qt.AscendingOrder) + treeWidget.header().setSortIndicator( + 0, QtCore.Qt.AscendingOrder) # init dictionary db = {} enabled = {} @@ -691,7 +694,8 @@ class MyForm(settingsmixin.SMainWindow): self.pushButtonStatusIcon = QtGui.QPushButton(self) self.pushButtonStatusIcon.setText('') - self.pushButtonStatusIcon.setIcon(QIcon(':/newPrefix/images/redicon.png')) + self.pushButtonStatusIcon.setIcon( + QtGui.QIcon(':/newPrefix/images/redicon.png')) self.pushButtonStatusIcon.setFlat(True) self.statusbar.insertPermanentWidget(0, self.pushButtonStatusIcon) QtCore.QObject.connect(self.pushButtonStatusIcon, QtCore.SIGNAL( @@ -1021,7 +1025,7 @@ class MyForm(settingsmixin.SMainWindow): l10n.formatTimestamp(lastactiontime)) newItem = myTableWidgetItem(statusText) newItem.setToolTip(statusText) - newItem.setData(Qt.UserRole, QByteArray(ackdata)) + newItem.setData(QtCore.Qt.UserRole, QtCore.QByteArray(ackdata)) newItem.setData(33, int(lastactiontime)) newItem.setFlags( QtCore.Qt.ItemIsSelectable | QtCore.Qt.ItemIsEnabled) @@ -1030,7 +1034,7 @@ class MyForm(settingsmixin.SMainWindow): return acct def addMessageListItemInbox(self, tableWidget, msgfolder, msgid, toAddress, fromAddress, subject, received, read): - font = QFont() + font = QtGui.QFont() font.setBold(True) if toAddress == str_broadcast_subscribers: acct = accountClass(fromAddress) @@ -1052,7 +1056,7 @@ class MyForm(settingsmixin.SMainWindow): # time received time_item = myTableWidgetItem(l10n.formatTimestamp(received)) time_item.setToolTip(l10n.formatTimestamp(received)) - time_item.setData(Qt.UserRole, QByteArray(msgid)) + time_item.setData(QtCore.Qt.UserRole, QtCore.QByteArray(msgid)) time_item.setData(33, int(received)) time_item.setFlags( QtCore.Qt.ItemIsSelectable | QtCore.Qt.ItemIsEnabled) @@ -1089,7 +1093,8 @@ class MyForm(settingsmixin.SMainWindow): toAddress, fromAddress, subject, status, ackdata, lastactiontime = row self.addMessageListItemSent(tableWidget, toAddress, fromAddress, subject, status, ackdata, lastactiontime) - tableWidget.horizontalHeader().setSortIndicator(3, Qt.DescendingOrder) + tableWidget.horizontalHeader().setSortIndicator( + 3, QtCore.Qt.DescendingOrder) tableWidget.setSortingEnabled(True) tableWidget.horizontalHeaderItem(3).setText(_translate("MainWindow", "Sent", None)) tableWidget.setUpdatesEnabled(True) @@ -1121,7 +1126,8 @@ class MyForm(settingsmixin.SMainWindow): msgfolder, msgid, toAddress, fromAddress, subject, received, read = row self.addMessageListItemInbox(tableWidget, msgfolder, msgid, toAddress, fromAddress, subject, received, read) - tableWidget.horizontalHeader().setSortIndicator(3, Qt.DescendingOrder) + tableWidget.horizontalHeader().setSortIndicator( + 3, QtCore.Qt.DescendingOrder) tableWidget.setSortingEnabled(True) tableWidget.selectRow(0) tableWidget.horizontalHeaderItem(3).setText(_translate("MainWindow", "Received", None)) @@ -1134,7 +1140,7 @@ class MyForm(settingsmixin.SMainWindow): QtCore.QObject.connect(self.tray, QtCore.SIGNAL( traySignal), self.__icon_activated) - m = QMenu() + m = QtGui.QMenu() self.actionStatus = QtGui.QAction(_translate( "MainWindow", "Not Connected"), m, checkable=False) @@ -1397,11 +1403,11 @@ class MyForm(settingsmixin.SMainWindow): # the same directory as this program. It is important that you # back up this file.', QMessageBox.Ok) reply = QtGui.QMessageBox.information(self, 'keys.dat?', _translate( - "MainWindow", "You may manage your keys by editing the keys.dat file stored in the same directory as this program. It is important that you back up this file."), QMessageBox.Ok) + "MainWindow", "You may manage your keys by editing the keys.dat file stored in the same directory as this program. It is important that you back up this file."), QtGui.QMessageBox.Ok) else: QtGui.QMessageBox.information(self, 'keys.dat?', _translate( - "MainWindow", "You may manage your keys by editing the keys.dat file stored in\n %1 \nIt is important that you back up this file.").arg(state.appdata), QMessageBox.Ok) + "MainWindow", "You may manage your keys by editing the keys.dat file stored in\n %1 \nIt is important that you back up this file.").arg(state.appdata), QtGui.QMessageBox.Ok) elif sys.platform == 'win32' or sys.platform == 'win64': if state.appdata == '': reply = QtGui.QMessageBox.question(self, _translate("MainWindow", "Open keys.dat?"), _translate( @@ -1432,7 +1438,7 @@ class MyForm(settingsmixin.SMainWindow): dialog = dialogs.RegenerateAddressesDialog(self) if dialog.exec_(): if dialog.lineEditPassphrase.text() == "": - QMessageBox.about( + QtGui.QMessageBox.about( self, _translate("MainWindow", "bad passphrase"), _translate( "MainWindow", @@ -1445,7 +1451,7 @@ class MyForm(settingsmixin.SMainWindow): addressVersionNumber = int( dialog.lineEditAddressVersionNumber.text()) except: - QMessageBox.about( + QtGui.QMessageBox.about( self, _translate("MainWindow", "Bad address version number"), _translate( @@ -1455,7 +1461,7 @@ class MyForm(settingsmixin.SMainWindow): )) return if addressVersionNumber < 3 or addressVersionNumber > 4: - QMessageBox.about( + QtGui.QMessageBox.about( self, _translate("MainWindow", "Bad address version number"), _translate( @@ -1509,7 +1515,7 @@ class MyForm(settingsmixin.SMainWindow): if event.type() == QtCore.QEvent.WindowStateChange: if self.windowState() & QtCore.Qt.WindowMinimized: if BMConfigParser().getboolean('bitmessagesettings', 'minimizetotray') and not 'darwin' in sys.platform: - QTimer.singleShot(0, self.appIndicatorHide) + QtCore.QTimer.singleShot(0, self.appIndicatorHide) elif event.oldState() & QtCore.Qt.WindowMinimized: # The window state has just been changed to # Normal/Maximised/FullScreen @@ -1531,7 +1537,7 @@ class MyForm(settingsmixin.SMainWindow): 'bitmessagesettings', 'hidetrayconnectionnotifications') if color == 'red': self.pushButtonStatusIcon.setIcon( - QIcon(":/newPrefix/images/redicon.png")) + QtGui.QIcon(":/newPrefix/images/redicon.png")) shared.statusIconColor = 'red' # if the connection is lost then show a notification if self.connected and _notifications_enabled: @@ -1552,8 +1558,8 @@ class MyForm(settingsmixin.SMainWindow): if color == 'yellow': if self.statusBar().currentMessage() == 'Warning: You are currently not connected. Bitmessage will do the work necessary to send the message but it won\'t send until you connect.': self.statusBar().clearMessage() - self.pushButtonStatusIcon.setIcon(QIcon( - ":/newPrefix/images/yellowicon.png")) + self.pushButtonStatusIcon.setIcon( + QtGui.QIcon(":/newPrefix/images/yellowicon.png")) shared.statusIconColor = 'yellow' # if a new connection has been established then show a notification if not self.connected and _notifications_enabled: @@ -1571,7 +1577,7 @@ class MyForm(settingsmixin.SMainWindow): if self.statusBar().currentMessage() == 'Warning: You are currently not connected. Bitmessage will do the work necessary to send the message but it won\'t send until you connect.': self.statusBar().clearMessage() self.pushButtonStatusIcon.setIcon( - QIcon(":/newPrefix/images/greenicon.png")) + QtGui.QIcon(":/newPrefix/images/greenicon.png")) shared.statusIconColor = 'green' if not self.connected and _notifications_enabled: self.notifierShow( @@ -1587,7 +1593,7 @@ class MyForm(settingsmixin.SMainWindow): def initTrayIcon(self, iconFileName, app): self.currentTrayIconFileName = iconFileName - self.tray = QSystemTrayIcon( + self.tray = QtGui.QSystemTrayIcon( self.calcTrayIcon(iconFileName, self.findInboxUnreadCount()), app) def setTrayIconFile(self, iconFileName): @@ -1616,9 +1622,10 @@ class MyForm(settingsmixin.SMainWindow): fontMetrics = QtGui.QFontMetrics(font) rect = fontMetrics.boundingRect(txt) # draw text - painter = QPainter() + painter = QtGui.QPainter() painter.begin(pixmap) - painter.setPen(QtGui.QPen(QtGui.QColor(255, 0, 0), Qt.SolidPattern)) + painter.setPen( + QtGui.QPen(QtGui.QColor(255, 0, 0), QtCore.Qt.SolidPattern)) painter.setFont(font) painter.drawText(24-rect.right()-marginX, -rect.top()+marginY, txt) painter.end() @@ -1627,13 +1634,14 @@ class MyForm(settingsmixin.SMainWindow): def drawTrayIcon(self, iconFileName, inboxUnreadCount): self.tray.setIcon(self.calcTrayIcon(iconFileName, inboxUnreadCount)) - def changedInboxUnread(self, row = None): - self.drawTrayIcon(self.currentTrayIconFileName, self.findInboxUnreadCount()) + def changedInboxUnread(self, row=None): + self.drawTrayIcon( + self.currentTrayIconFileName, self.findInboxUnreadCount()) self.rerenderTabTreeMessages() self.rerenderTabTreeSubscriptions() self.rerenderTabTreeChans() - def findInboxUnreadCount(self, count = None): + def findInboxUnreadCount(self, count=None): if count is None: queryreturn = sqlQuery('''SELECT count(*) from inbox WHERE folder='inbox' and read=0''') cnt = 0 @@ -1653,8 +1661,7 @@ class MyForm(settingsmixin.SMainWindow): continue for i in range(sent.rowCount()): - rowAddress = sent.item( - i, 0).data(Qt.UserRole) + rowAddress = sent.item(i, 0).data(QtCore.Qt.UserRole) if toAddress == rowAddress: sent.item(i, 3).setToolTip(textToDisplay) try: @@ -1674,9 +1681,9 @@ class MyForm(settingsmixin.SMainWindow): continue for i in range(sent.rowCount()): toAddress = sent.item( - i, 0).data(Qt.UserRole) + i, 0).data(QtCore.Qt.UserRole) tableAckdata = sent.item( - i, 3).data(Qt.UserRole).toPyObject() + i, 3).data(QtCore.Qt.UserRole).toPyObject() status, addressVersionNumber, streamNumber, ripe = decodeAddress( toAddress) if ackdata == tableAckdata: @@ -1697,11 +1704,11 @@ class MyForm(settingsmixin.SMainWindow): self.ui.tableWidgetInboxSubscriptions, self.ui.tableWidgetInboxChans]): for i in range(inbox.rowCount()): - if msgid == str(inbox.item(i, 3).data(Qt.UserRole).toPyObject()): + if msgid == str(inbox.item(i, 3).data(QtCore.Qt.UserRole).toPyObject()): self.statusBar().showMessage(_translate( "MainWindow", "Message trashed"), 10000) treeWidget = self.widgetConvert(inbox) - self.propagateUnreadCount(inbox.item(i, 1 if inbox.item(i, 1).type == AccountMixin.SUBSCRIPTION else 0).data(Qt.UserRole), self.getCurrentFolder(treeWidget), treeWidget, 0) + self.propagateUnreadCount(inbox.item(i, 1 if inbox.item(i, 1).type == AccountMixin.SUBSCRIPTION else 0).data(QtCore.Qt.UserRole), self.getCurrentFolder(treeWidget), treeWidget, 0) inbox.removeRow(i) break @@ -1711,7 +1718,7 @@ class MyForm(settingsmixin.SMainWindow): def displayAlert(self, title, text, exitAfterUserClicksOk): self.statusBar().showMessage(text) - QtGui.QMessageBox.critical(self, title, text, QMessageBox.Ok) + QtGui.QMessageBox.critical(self, title, text, QtGui.QMessageBox.Ok) if exitAfterUserClicksOk: os._exit(0) @@ -1739,7 +1746,7 @@ class MyForm(settingsmixin.SMainWindow): oldRows[item.address] = [item.label, item.type, i] if self.ui.tableWidgetAddressBook.rowCount() == 0: - self.ui.tableWidgetAddressBook.horizontalHeader().setSortIndicator(0, Qt.AscendingOrder) + self.ui.tableWidgetAddressBook.horizontalHeader().setSortIndicator(0, QtCore.Qt.AscendingOrder) if self.ui.tableWidgetAddressBook.isSortingEnabled(): self.ui.tableWidgetAddressBook.setSortingEnabled(False) @@ -1773,7 +1780,8 @@ class MyForm(settingsmixin.SMainWindow): completerList.append(unicode(newRows[address][0], encoding="UTF-8") + " <" + address + ">") # sort - self.ui.tableWidgetAddressBook.sortByColumn(0, Qt.AscendingOrder) + self.ui.tableWidgetAddressBook.sortByColumn( + 0, QtCore.Qt.AscendingOrder) self.ui.tableWidgetAddressBook.setSortingEnabled(True) self.ui.lineEditTo.completer().model().setStringList(completerList) @@ -1786,7 +1794,7 @@ class MyForm(settingsmixin.SMainWindow): "MainWindow", """The TTL, or Time-To-Live is the length of time that the network will hold the message. The recipient must get it during this time. If your Bitmessage client does not hear an acknowledgement, it will resend the message automatically. The longer the Time-To-Live, the - more work your computer must do to send the message. A Time-To-Live of four or five days is often appropriate."""), QMessageBox.Ok) + more work your computer must do to send the message. A Time-To-Live of four or five days is often appropriate."""), QtGui.QMessageBox.Ok) def click_pushButtonSend(self): encoding = 3 if QtGui.QApplication.queryKeyboardModifiers() & QtCore.Qt.ShiftModifier else 2 @@ -1798,8 +1806,8 @@ class MyForm(settingsmixin.SMainWindow): # message to specific people sendMessageToPeople = True fromAddress = str(self.ui.comboBoxSendFrom.itemData( - self.ui.comboBoxSendFrom.currentIndex(), - Qt.UserRole).toString()) + self.ui.comboBoxSendFrom.currentIndex(), + QtCore.Qt.UserRole).toString()) toAddresses = str(self.ui.lineEditTo.text().toUtf8()) subject = str(self.ui.lineEditSubject.text().toUtf8()) message = str( @@ -1808,23 +1816,29 @@ class MyForm(settingsmixin.SMainWindow): # broadcast message sendMessageToPeople = False fromAddress = str(self.ui.comboBoxSendFromBroadcast.itemData( - self.ui.comboBoxSendFromBroadcast.currentIndex(), - Qt.UserRole).toString()) + self.ui.comboBoxSendFromBroadcast.currentIndex(), + QtCore.Qt.UserRole).toString()) subject = str(self.ui.lineEditSubjectBroadcast.text().toUtf8()) message = str( self.ui.textEditMessageBroadcast.document().toPlainText().toUtf8()) """ - The whole network message must fit in 2^18 bytes. Let's assume 500 - bytes of overhead. If someone wants to get that too an exact - number you are welcome to but I think that it would be a better - use of time to support message continuation so that users can - send messages of any length. + The whole network message must fit in 2^18 bytes. + Let's assume 500 bytes of overhead. If someone wants to get that + too an exact number you are welcome to but I think that it would + be a better use of time to support message continuation so that + users can send messages of any length. """ - if len(message) > (2 ** 18 - 500): - QMessageBox.about(self, _translate("MainWindow", "Message too long"), _translate( - "MainWindow", "The message that you are trying to send is too long by %1 bytes. (The maximum is 261644 bytes). Please cut it down before sending.").arg(len(message) - (2 ** 18 - 500))) + if len(message) > (2 ** 18 - 500): + QtGui.QMessageBox.about( + self, _translate("MainWindow", "Message too long"), + _translate( + "MainWindow", + "The message that you are trying to send is too long" + " by %1 bytes. (The maximum is 261644 bytes). Please" + " cut it down before sending." + ).arg(len(message) - (2 ** 18 - 500))) return - + acct = accountClass(fromAddress) if sendMessageToPeople: # To send a message to specific people (rather than broadcast) @@ -1900,11 +1914,11 @@ class MyForm(settingsmixin.SMainWindow): toAddress = addBMIfNotPresent(toAddress) if addressVersionNumber > 4 or addressVersionNumber <= 1: - QMessageBox.about(self, _translate("MainWindow", "Address version number"), _translate( + QtGui.QMessageBox.about(self, _translate("MainWindow", "Address version number"), _translate( "MainWindow", "Concerning the address %1, Bitmessage cannot understand address version numbers of %2. Perhaps upgrade Bitmessage to the latest version.").arg(toAddress).arg(str(addressVersionNumber))) continue if streamNumber > 1 or streamNumber == 0: - QMessageBox.about(self, _translate("MainWindow", "Stream number"), _translate( + QtGui.QMessageBox.about(self, _translate("MainWindow", "Stream number"), _translate( "MainWindow", "Concerning the address %1, Bitmessage cannot handle stream numbers of %2. Perhaps upgrade Bitmessage to the latest version.").arg(toAddress).arg(str(streamNumber))) continue self.statusBar().clearMessage() @@ -2050,8 +2064,11 @@ class MyForm(settingsmixin.SMainWindow): self.ui.comboBoxSendFrom.addItem(avatarize(addressInKeysFile), label, addressInKeysFile) # self.ui.comboBoxSendFrom.model().sort(1, Qt.AscendingOrder) for i in range(self.ui.comboBoxSendFrom.count()): - address = str(self.ui.comboBoxSendFrom.itemData(i, Qt.UserRole).toString()) - self.ui.comboBoxSendFrom.setItemData(i, AccountColor(address).accountColor(), Qt.ForegroundRole) + address = str(self.ui.comboBoxSendFrom.itemData( + i, QtCore.Qt.UserRole).toString()) + self.ui.comboBoxSendFrom.setItemData( + i, AccountColor(address).accountColor(), + QtCore.Qt.ForegroundRole) self.ui.comboBoxSendFrom.insertItem(0, '', '') if(self.ui.comboBoxSendFrom.count() == 2): self.ui.comboBoxSendFrom.setCurrentIndex(1) @@ -2070,8 +2087,11 @@ class MyForm(settingsmixin.SMainWindow): label = addressInKeysFile self.ui.comboBoxSendFromBroadcast.addItem(avatarize(addressInKeysFile), label, addressInKeysFile) for i in range(self.ui.comboBoxSendFromBroadcast.count()): - address = str(self.ui.comboBoxSendFromBroadcast.itemData(i, Qt.UserRole).toString()) - self.ui.comboBoxSendFromBroadcast.setItemData(i, AccountColor(address).accountColor(), Qt.ForegroundRole) + address = str(self.ui.comboBoxSendFromBroadcast.itemData( + i, QtCore.Qt.UserRole).toString()) + self.ui.comboBoxSendFromBroadcast.setItemData( + i, AccountColor(address).accountColor(), + QtCore.Qt.ForegroundRole) self.ui.comboBoxSendFromBroadcast.insertItem(0, '', '') if(self.ui.comboBoxSendFromBroadcast.count() == 2): self.ui.comboBoxSendFromBroadcast.setCurrentIndex(1) @@ -2285,7 +2305,7 @@ class MyForm(settingsmixin.SMainWindow): if int(BMConfigParser().get('bitmessagesettings', 'port')) != int(self.settingsDialogInstance.ui.lineEditTCPPort.text()): if not BMConfigParser().safeGetBoolean('bitmessagesettings', 'dontconnect'): - QMessageBox.about(self, _translate("MainWindow", "Restart"), _translate( + QtGui.QMessageBox.about(self, _translate("MainWindow", "Restart"), _translate( "MainWindow", "You must restart Bitmessage for the port number change to take effect.")) BMConfigParser().set('bitmessagesettings', 'port', str( self.settingsDialogInstance.ui.lineEditTCPPort.text())) @@ -2299,7 +2319,7 @@ class MyForm(settingsmixin.SMainWindow): #print 'self.settingsDialogInstance.ui.comboBoxProxyType.currentText())[0:5]', self.settingsDialogInstance.ui.comboBoxProxyType.currentText()[0:5] if BMConfigParser().get('bitmessagesettings', 'socksproxytype') == 'none' and self.settingsDialogInstance.ui.comboBoxProxyType.currentText()[0:5] == 'SOCKS': if shared.statusIconColor != 'red': - QMessageBox.about(self, _translate("MainWindow", "Restart"), _translate( + QtGui.QMessageBox.about(self, _translate("MainWindow", "Restart"), _translate( "MainWindow", "Bitmessage will use your proxy from now on but you may want to manually restart Bitmessage now to close existing connections (if any).")) if BMConfigParser().get('bitmessagesettings', 'socksproxytype')[0:5] == 'SOCKS' and self.settingsDialogInstance.ui.comboBoxProxyType.currentText()[0:5] != 'SOCKS': self.statusBar().clearMessage() @@ -2328,7 +2348,7 @@ class MyForm(settingsmixin.SMainWindow): BMConfigParser().set('bitmessagesettings', 'maxuploadrate', str( int(float(self.settingsDialogInstance.ui.lineEditMaxUploadRate.text())))) except ValueError: - QMessageBox.about(self, _translate("MainWindow", "Number needed"), _translate( + QtGui.QMessageBox.about(self, _translate("MainWindow", "Number needed"), _translate( "MainWindow", "Your maximum download and upload rate must be numbers. Ignoring what you typed.")) else: set_rates(BMConfigParser().safeGetInt("bitmessagesettings", "maxdownloadrate"), @@ -2408,7 +2428,7 @@ class MyForm(settingsmixin.SMainWindow): if (float(self.settingsDialogInstance.ui.lineEditDays.text()) >=0 and float(self.settingsDialogInstance.ui.lineEditMonths.text()) >=0): shared.maximumLengthOfTimeToBotherResendingMessages = (float(str(self.settingsDialogInstance.ui.lineEditDays.text())) * 24 * 60 * 60) + (float(str(self.settingsDialogInstance.ui.lineEditMonths.text())) * (60 * 60 * 24 *365)/12) if shared.maximumLengthOfTimeToBotherResendingMessages < 432000: # If the time period is less than 5 hours, we give zero values to all fields. No message will be sent again. - QMessageBox.about(self, _translate("MainWindow", "Will not resend ever"), _translate( + QtGui.QMessageBox.about(self, _translate("MainWindow", "Will not resend ever"), _translate( "MainWindow", "Note that the time limit you entered is less than the amount of time Bitmessage waits for the first resend attempt therefore your messages will never be resent.")) BMConfigParser().set('bitmessagesettings', 'stopresendingafterxdays', '0') BMConfigParser().set('bitmessagesettings', 'stopresendingafterxmonths', '0') @@ -2508,7 +2528,8 @@ class MyForm(settingsmixin.SMainWindow): _translate( "MainWindow", "Are you sure you would like to mark all messages read?" - ), QMessageBox.Yes | QMessageBox.No) != QMessageBox.Yes: + ), QtGui.QMessageBox.Yes | QtGui.QMessageBox.No + ) != QtGui.QMessageBox.Yes: return addressAtCurrentRow = self.getCurrentAccount() tableWidget = self.getCurrentMessagelist() @@ -2517,13 +2538,13 @@ class MyForm(settingsmixin.SMainWindow): if idCount == 0: return - font = QFont() + font = QtGui.QFont() font.setBold(False) msgids = [] for i in range(0, idCount): msgids.append(str(tableWidget.item( - i, 3).data(Qt.UserRole).toPyObject())) + i, 3).data(QtCore.Qt.UserRole).toPyObject())) tableWidget.item(i, 0).setUnread(False) tableWidget.item(i, 1).setUnread(False) tableWidget.item(i, 2).setUnread(False) @@ -2564,7 +2585,7 @@ class MyForm(settingsmixin.SMainWindow): else: if dialog.lineEditPassphrase.text() != \ dialog.lineEditPassphraseAgain.text(): - QMessageBox.about( + QtGui.QMessageBox.about( self, _translate("MainWindow", "Passphrase mismatch"), _translate( "MainWindow", @@ -2572,7 +2593,7 @@ class MyForm(settingsmixin.SMainWindow): " match. Try again.") ) elif dialog.lineEditPassphrase.text() == "": - QMessageBox.about( + QtGui.QMessageBox.about( self, _translate("MainWindow", "Choose a passphrase"), _translate( "MainWindow", "You really do need a passphrase.") @@ -2780,14 +2801,14 @@ class MyForm(settingsmixin.SMainWindow): tableWidget = self.getCurrentMessagelist() if not tableWidget: return - font = QFont() + font = QtGui.QFont() font.setBold(True) inventoryHashesToMarkUnread = [] modified = 0 for row in tableWidget.selectedIndexes(): currentRow = row.row() inventoryHashToMarkUnread = str(tableWidget.item( - currentRow, 3).data(Qt.UserRole).toPyObject()) + currentRow, 3).data(QtCore.Qt.UserRole).toPyObject()) if inventoryHashToMarkUnread in inventoryHashesToMarkUnread: # it returns columns as separate items, so we skip dupes continue @@ -2807,9 +2828,9 @@ class MyForm(settingsmixin.SMainWindow): if rowcount == 1: # performance optimisation - self.propagateUnreadCount(tableWidget.item(currentRow, 1 if tableWidget.item(currentRow, 1).type == AccountMixin.SUBSCRIPTION else 0).data(Qt.UserRole), self.getCurrentFolder()) + self.propagateUnreadCount(tableWidget.item(currentRow, 1 if tableWidget.item(currentRow, 1).type == AccountMixin.SUBSCRIPTION else 0).data(QtCore.Qt.UserRole), self.getCurrentFolder()) else: - self.propagateUnreadCount(tableWidget.item(currentRow, 1 if tableWidget.item(currentRow, 1).type == AccountMixin.SUBSCRIPTION else 0).data(Qt.UserRole), self.getCurrentFolder(), self.getCurrentTreeWidget(), 0) + self.propagateUnreadCount(tableWidget.item(currentRow, 1 if tableWidget.item(currentRow, 1).type == AccountMixin.SUBSCRIPTION else 0).data(QtCore.Qt.UserRole), self.getCurrentFolder(), self.getCurrentTreeWidget(), 0) # tableWidget.selectRow(currentRow + 1) # This doesn't de-select the last message if you try to mark it unread, but that doesn't interfere. Might not be necessary. # We could also select upwards, but then our problem would be with the topmost message. @@ -2873,7 +2894,7 @@ class MyForm(settingsmixin.SMainWindow): fromAddressAtCurrentInboxRow = tableWidget.item( currentInboxRow, 1).address msgid = str(tableWidget.item( - currentInboxRow, 3).data(Qt.UserRole).toPyObject()) + currentInboxRow, 3).data(QtCore.Qt.UserRole).toPyObject()) queryreturn = sqlQuery( '''select message from inbox where msgid=?''', msgid) if queryreturn != []: @@ -2892,10 +2913,10 @@ class MyForm(settingsmixin.SMainWindow): # toAddressAtCurrentInboxRow = fromAddressAtCurrentInboxRow elif not BMConfigParser().has_section(toAddressAtCurrentInboxRow): QtGui.QMessageBox.information(self, _translate("MainWindow", "Address is gone"), _translate( - "MainWindow", "Bitmessage cannot find your address %1. Perhaps you removed it?").arg(toAddressAtCurrentInboxRow), QMessageBox.Ok) + "MainWindow", "Bitmessage cannot find your address %1. Perhaps you removed it?").arg(toAddressAtCurrentInboxRow), QtGui.QMessageBox.Ok) elif not BMConfigParser().getboolean(toAddressAtCurrentInboxRow, 'enabled'): QtGui.QMessageBox.information(self, _translate("MainWindow", "Address disabled"), _translate( - "MainWindow", "Error: The address from which you are trying to send is disabled. You\'ll have to enable it on the \'Your Identities\' tab before using it."), QMessageBox.Ok) + "MainWindow", "Error: The address from which you are trying to send is disabled. You\'ll have to enable it on the \'Your Identities\' tab before using it."), QtGui.QMessageBox.Ok) else: self.setBroadcastEnablementDependingOnWhetherThisIsAMailingListAddress(toAddressAtCurrentInboxRow) broadcast_tab_index = self.ui.tabWidgetSend.indexOf( @@ -2943,7 +2964,7 @@ class MyForm(settingsmixin.SMainWindow): currentInboxRow = tableWidget.currentRow() # tableWidget.item(currentRow,1).data(Qt.UserRole).toPyObject() addressAtCurrentInboxRow = tableWidget.item( - currentInboxRow, 1).data(Qt.UserRole) + currentInboxRow, 1).data(QtCore.Qt.UserRole) self.ui.tabWidget.setCurrentIndex( self.ui.tabWidget.indexOf(self.ui.send) ) @@ -2958,9 +2979,9 @@ class MyForm(settingsmixin.SMainWindow): currentInboxRow = tableWidget.currentRow() # tableWidget.item(currentRow,1).data(Qt.UserRole).toPyObject() addressAtCurrentInboxRow = tableWidget.item( - currentInboxRow, 1).data(Qt.UserRole) + currentInboxRow, 1).data(QtCore.Qt.UserRole) recipientAddress = tableWidget.item( - currentInboxRow, 0).data(Qt.UserRole) + currentInboxRow, 0).data(QtCore.Qt.UserRole) # Let's make sure that it isn't already in the address book queryreturn = sqlQuery('''select * from blacklist where address=?''', addressAtCurrentInboxRow) @@ -2983,15 +3004,16 @@ class MyForm(settingsmixin.SMainWindow): messageLists = (messageLists) for messageList in messageLists: if row is not None: - inventoryHash = str(messageList.item(row, 3).data(Qt.UserRole).toPyObject()) + inventoryHash = str(messageList.item(row, 3).data( + QtCore.Qt.UserRole).toPyObject()) messageList.removeRow(row) elif inventoryHash is not None: for i in range(messageList.rowCount() - 1, -1, -1): - if messageList.item(i, 3).data(Qt.UserRole).toPyObject() == inventoryHash: + if messageList.item(i, 3).data(QtCore.Qt.UserRole).toPyObject() == inventoryHash: messageList.removeRow(i) elif ackData is not None: for i in range(messageList.rowCount() - 1, -1, -1): - if messageList.item(i, 3).data(Qt.UserRole).toPyObject() == ackData: + if messageList.item(i, 3).data(QtCore.Qt.UserRole).toPyObject() == ackData: messageList.removeRow(i) # Send item on the Inbox tab to trash @@ -3065,13 +3087,14 @@ class MyForm(settingsmixin.SMainWindow): return currentInboxRow = tableWidget.currentRow() try: - subjectAtCurrentInboxRow = str(tableWidget.item(currentInboxRow,2).data(Qt.UserRole)) + subjectAtCurrentInboxRow = str(tableWidget.item( + currentInboxRow, 2).data(QtCore.Qt.UserRole)) except: subjectAtCurrentInboxRow = '' # Retrieve the message data out of the SQL database msgid = str(tableWidget.item( - currentInboxRow, 3).data(Qt.UserRole).toPyObject()) + currentInboxRow, 3).data(QtCore.Qt.UserRole).toPyObject()) queryreturn = sqlQuery( '''select message from inbox where msgid=?''', msgid) if queryreturn != []: @@ -3079,7 +3102,7 @@ class MyForm(settingsmixin.SMainWindow): message, = row defaultFilename = "".join(x for x in subjectAtCurrentInboxRow if x.isalnum()) + '.txt' - filename = QFileDialog.getSaveFileName(self, _translate("MainWindow","Save As..."), defaultFilename, "Text files (*.txt);;All files (*.*)") + filename = QtGui.QFileDialog.getSaveFileName(self, _translate("MainWindow","Save As..."), defaultFilename, "Text files (*.txt);;All files (*.*)") if filename == '': return try: @@ -3102,13 +3125,13 @@ class MyForm(settingsmixin.SMainWindow): while tableWidget.selectedIndexes() != []: currentRow = tableWidget.selectedIndexes()[0].row() ackdataToTrash = str(tableWidget.item( - currentRow, 3).data(Qt.UserRole).toPyObject()) + currentRow, 3).data(QtCore.Qt.UserRole).toPyObject()) if folder == "trash" or shifted: sqlExecute('''DELETE FROM sent WHERE ackdata=?''', ackdataToTrash) else: sqlExecute('''UPDATE sent SET folder='trash' WHERE ackdata=?''', ackdataToTrash) if tableWidget.item(currentRow, 0).unread: - self.propagateUnreadCount(tableWidget.item(currentRow, 1 if tableWidget.item(currentRow, 1).type == AccountMixin.SUBSCRIPTION else 0).data(Qt.UserRole), folder, self.getCurrentTreeWidget(), -1) + self.propagateUnreadCount(tableWidget.item(currentRow, 1 if tableWidget.item(currentRow, 1).type == AccountMixin.SUBSCRIPTION else 0).data(QtCore.Qt.UserRole), folder, self.getCurrentTreeWidget(), -1) self.getCurrentMessageTextedit().setPlainText("") tableWidget.removeRow(currentRow) self.statusBar().showMessage(_translate( @@ -3121,7 +3144,7 @@ class MyForm(settingsmixin.SMainWindow): def on_action_ForceSend(self): currentRow = self.ui.tableWidgetInbox.currentRow() addressAtCurrentRow = self.ui.tableWidgetInbox.item( - currentRow, 0).data(Qt.UserRole) + currentRow, 0).data(QtCore.Qt.UserRole) toRipe = decodeAddress(addressAtCurrentRow)[3] sqlExecute( '''UPDATE sent SET status='forcepow' WHERE toripe=? AND status='toodifficult' and folder='sent' ''', @@ -3136,7 +3159,7 @@ class MyForm(settingsmixin.SMainWindow): def on_action_SentClipboard(self): currentRow = self.ui.tableWidgetInbox.currentRow() addressAtCurrentRow = self.ui.tableWidgetInbox.item( - currentRow, 0).data(Qt.UserRole) + currentRow, 0).data(QtCore.Qt.UserRole) clipboard = QtGui.QApplication.clipboard() clipboard.setText(str(addressAtCurrentRow)) @@ -3207,7 +3230,11 @@ class MyForm(settingsmixin.SMainWindow): addressAtCurrentRow = str(self.ui.tableWidgetAddressBook.item(currentRow,1).text()) # Then subscribe to it... provided it's not already in the address book if shared.isAddressInMySubscriptionsList(addressAtCurrentRow): - self.statusBar().showMessage(QtGui.QApplication.translate("MainWindow", "Error: You cannot add the same address to your subscriptions twice. Perhaps rename the existing one if you want."), 10000) + self.statusBar().showMessage(_translate( + "MainWindow", + "Error: You cannot add the same address to your" + " subscriptions twice. Perhaps rename the existing" + " one if you want."), 10000) continue labelAtCurrentRow = self.ui.tableWidgetAddressBook.item(currentRow,0).text().toUtf8() self.addSubscription(addressAtCurrentRow, labelAtCurrentRow) @@ -3240,9 +3267,21 @@ class MyForm(settingsmixin.SMainWindow): # Group of functions for the Subscriptions dialog box def on_action_SubscriptionsNew(self): self.click_pushButtonAddSubscription() - + def on_action_SubscriptionsDelete(self): - if QtGui.QMessageBox.question(self, "Delete subscription?", _translate("MainWindow", "If you delete the subscription, messages that you already received will become inaccessible. Maybe you can consider disabling the subscription instead. Disabled subscriptions will not receive new messages, but you can still view messages you already received.\n\nAre you sure you want to delete the subscription?"), QMessageBox.Yes|QMessageBox.No) != QMessageBox.Yes: + if QtGui.QMessageBox.question( + self, "Delete subscription?", + _translate( + "MainWindow", + "If you delete the subscription, messages that you" + " already received will become inaccessible. Maybe" + " you can consider disabling the subscription instead." + " Disabled subscriptions will not receive new" + " messages, but you can still view messages you" + " already received.\n\nAre you sure you want to" + " delete the subscription?" + ), QtGui.QMessageBox.Yes | QtGui.QMessageBox.No + ) != QtGui.QMessageBox.Yes: return address = self.getCurrentAccount() sqlExecute('''DELETE FROM subscriptions WHERE address=?''', @@ -3366,12 +3405,13 @@ class MyForm(settingsmixin.SMainWindow): currentRow = messagelist.currentRow() if currentRow >= 0: msgid = str(messagelist.item( - currentRow, 3).data(Qt.UserRole).toPyObject()) # data is saved at the 4. column of the table... + currentRow, 3).data(QtCore.Qt.UserRole).toPyObject()) + # data is saved at the 4. column of the table... return msgid return False def getCurrentMessageTextedit(self): - currentIndex = self.ui.tabWidget.currentIndex(); + currentIndex = self.ui.tabWidget.currentIndex() messagelistList = [ self.ui.textEditInboxMessage, False, @@ -3394,9 +3434,9 @@ class MyForm(settingsmixin.SMainWindow): except: return self.ui.textEditInboxMessage - def getCurrentSearchLine(self, currentIndex = None, retObj = False): + def getCurrentSearchLine(self, currentIndex=None, retObj=False): if currentIndex is None: - currentIndex = self.ui.tabWidget.currentIndex(); + currentIndex = self.ui.tabWidget.currentIndex() messagelistList = [ self.ui.inboxSearchLineEdit, False, @@ -3411,9 +3451,9 @@ class MyForm(settingsmixin.SMainWindow): else: return None - def getCurrentSearchOption(self, currentIndex = None): + def getCurrentSearchOption(self, currentIndex=None): if currentIndex is None: - currentIndex = self.ui.tabWidget.currentIndex(); + currentIndex = self.ui.tabWidget.currentIndex() messagelistList = [ self.ui.inboxSearchOption, False, @@ -3426,7 +3466,7 @@ class MyForm(settingsmixin.SMainWindow): return None # Group of functions for the Your Identities dialog box - def getCurrentItem(self, treeWidget = None): + def getCurrentItem(self, treeWidget=None): if treeWidget is None: treeWidget = self.getCurrentTreeWidget() if treeWidget: @@ -3435,7 +3475,7 @@ class MyForm(settingsmixin.SMainWindow): return currentItem return False - def getCurrentAccount(self, treeWidget = None): + def getCurrentAccount(self, treeWidget=None): currentItem = self.getCurrentItem(treeWidget) if currentItem: account = currentItem.address @@ -3444,7 +3484,7 @@ class MyForm(settingsmixin.SMainWindow): # TODO need debug msg? return False - def getCurrentFolder(self, treeWidget = None): + def getCurrentFolder(self, treeWidget=None): if treeWidget is None: treeWidget = self.getCurrentTreeWidget() #treeWidget = self.ui.treeWidgetYourIdentities @@ -3470,9 +3510,21 @@ class MyForm(settingsmixin.SMainWindow): def on_action_YourIdentitiesDelete(self): account = self.getCurrentItem() if account.type == AccountMixin.NORMAL: - return # maybe in the future + return # maybe in the future elif account.type == AccountMixin.CHAN: - if QtGui.QMessageBox.question(self, "Delete channel?", _translate("MainWindow", "If you delete the channel, messages that you already received will become inaccessible. Maybe you can consider disabling the channel instead. Disabled channels will not receive new messages, but you can still view messages you already received.\n\nAre you sure you want to delete the channel?"), QMessageBox.Yes|QMessageBox.No) == QMessageBox.Yes: + if QtGui.QMessageBox.question( + self, "Delete channel?", + _translate( + "MainWindow", + "If you delete the channel, messages that you" + " already received will become inaccessible." + " Maybe you can consider disabling the channel" + " instead. Disabled channels will not receive new" + " messages, but you can still view messages you" + " already received.\n\nAre you sure you want to" + " delete the channel?" + ), QtGui.QMessageBox.Yes | QtGui.QMessageBox.No + ) == QtGui.QMessageBox.Yes: BMConfigParser().remove_section(str(account.address)) else: return @@ -3526,18 +3578,18 @@ class MyForm(settingsmixin.SMainWindow): else: currentColumn = 1 if self.getCurrentFolder() == "sent": - myAddress = tableWidget.item(currentRow, 1).data(Qt.UserRole) - otherAddress = tableWidget.item(currentRow, 0).data(Qt.UserRole) + myAddress = tableWidget.item(currentRow, 1).data(QtCore.Qt.UserRole) + otherAddress = tableWidget.item(currentRow, 0).data(QtCore.Qt.UserRole) else: - myAddress = tableWidget.item(currentRow, 0).data(Qt.UserRole) - otherAddress = tableWidget.item(currentRow, 1).data(Qt.UserRole) + myAddress = tableWidget.item(currentRow, 0).data(QtCore.Qt.UserRole) + otherAddress = tableWidget.item(currentRow, 1).data(QtCore.Qt.UserRole) account = accountClass(myAddress) if isinstance(account, GatewayAccount) and otherAddress == account.relayAddress and ( (currentColumn in [0, 2] and self.getCurrentFolder() == "sent") or (currentColumn in [1, 2] and self.getCurrentFolder() != "sent")): text = str(tableWidget.item(currentRow, currentColumn).label) else: - text = tableWidget.item(currentRow, currentColumn).data(Qt.UserRole) + text = tableWidget.item(currentRow, currentColumn).data(QtCore.Qt.UserRole) text = unicode(str(text), 'utf-8', 'ignore') clipboard = QtGui.QApplication.clipboard() clipboard.setText(text) @@ -3580,7 +3632,10 @@ class MyForm(settingsmixin.SMainWindow): current_files += [upper] filters[0:0] = ['Image files (' + ' '.join(all_images_filter) + ')'] filters[1:1] = ['All files (*.*)'] - sourcefile = QFileDialog.getOpenFileName(self, _translate("MainWindow","Set avatar..."), filter = ';;'.join(filters)) + sourcefile = QtGui.QFileDialog.getOpenFileName( + self, _translate("MainWindow", "Set avatar..."), + filter = ';;'.join(filters) + ) # determine the correct filename (note that avatars don't use the suffix) destination = state.appdata + 'avatars/' + hash + '.' + sourcefile.split('.')[-1] exists = QtCore.QFile.exists(destination) @@ -3730,7 +3785,7 @@ class MyForm(settingsmixin.SMainWindow): self.popMenuInbox.addAction(self.actionMarkUnread) self.popMenuInbox.addSeparator() address = tableWidget.item( - tableWidget.currentRow(), 0).data(Qt.UserRole) + tableWidget.currentRow(), 0).data(QtCore.Qt.UserRole) account = accountClass(address) if account.type == AccountMixin.CHAN: self.popMenuInbox.addAction(self.actionReplyChan) @@ -3762,7 +3817,7 @@ class MyForm(settingsmixin.SMainWindow): currentRow = self.ui.tableWidgetInbox.currentRow() if currentRow >= 0: ackData = str(self.ui.tableWidgetInbox.item( - currentRow, 3).data(Qt.UserRole).toPyObject()) + currentRow, 3).data(QtCore.Qt.UserRole).toPyObject()) queryreturn = sqlQuery('''SELECT status FROM sent where ackdata=?''', ackData) for row in queryreturn: status, = row @@ -3799,7 +3854,7 @@ class MyForm(settingsmixin.SMainWindow): searchOption = self.getCurrentSearchOption() messageTextedit = self.getCurrentMessageTextedit() if messageTextedit: - messageTextedit.setPlainText(QString("")) + messageTextedit.setPlainText(QtCore.QString("")) messagelist = self.getCurrentMessagelist() if messagelist: account = self.getCurrentAccount() @@ -3885,7 +3940,7 @@ class MyForm(settingsmixin.SMainWindow): if refresh: if not tableWidget: return - font = QFont() + font = QtGui.QFont() font.setBold(False) # inventoryHashesToMarkRead = [] # inventoryHashToMarkRead = str(tableWidget.item( @@ -3896,7 +3951,7 @@ class MyForm(settingsmixin.SMainWindow): tableWidget.item(currentRow, 2).setUnread(False) tableWidget.item(currentRow, 3).setFont(font) if propagate: - self.propagateUnreadCount(tableWidget.item(currentRow, 1 if tableWidget.item(currentRow, 1).type == AccountMixin.SUBSCRIPTION else 0).data(Qt.UserRole), folder, self.getCurrentTreeWidget(), -1) + self.propagateUnreadCount(tableWidget.item(currentRow, 1 if tableWidget.item(currentRow, 1).type == AccountMixin.SUBSCRIPTION else 0).data(QtCore.Qt.UserRole), folder, self.getCurrentTreeWidget(), -1) else: data = self.getCurrentMessageId() @@ -4188,7 +4243,7 @@ class settingsDialog(QtGui.QDialog): # In order for the time columns on the Inbox and Sent tabs to be sorted # correctly (rather than alphabetically), we need to overload the < # operator and use this class instead of QTableWidgetItem. -class myTableWidgetItem(QTableWidgetItem): +class myTableWidgetItem(QtGui.QTableWidgetItem): def __lt__(self, other): return int(self.data(33).toPyObject()) < int(other.data(33).toPyObject()) @@ -4197,7 +4252,8 @@ class myTableWidgetItem(QTableWidgetItem): app = None myapp = None -class MySingleApplication(QApplication): + +class MySingleApplication(QtGui.QApplication): """ Listener to allow our Qt form to get focus when another instance of the application is open. @@ -4247,12 +4303,14 @@ class MySingleApplication(QApplication): if myapp: myapp.appIndicatorShow() + def init(): global app if not app: app = MySingleApplication(sys.argv) return app + def run(): global myapp app = init() From ece5ad91137cdbd9723b7493614ab2dd766045d5 Mon Sep 17 00:00:00 2001 From: Dmitri Bogomolov <4glitch@gmail.com> Date: Tue, 23 Jan 2018 11:48:59 +0200 Subject: [PATCH 348/407] Separated dialogs which deal with addresses into address_dialogs module --- src/bitmessageqt/address_dialogs.py | 274 +++++++++++++++++++++++++++ src/bitmessageqt/dialogs.py | 281 +--------------------------- 2 files changed, 284 insertions(+), 271 deletions(-) create mode 100644 src/bitmessageqt/address_dialogs.py diff --git a/src/bitmessageqt/address_dialogs.py b/src/bitmessageqt/address_dialogs.py new file mode 100644 index 00000000..0492dbec --- /dev/null +++ b/src/bitmessageqt/address_dialogs.py @@ -0,0 +1,274 @@ +from PyQt4 import QtCore, QtGui +from addresses import decodeAddress, encodeVarint +from account import ( + GatewayAccount, MailchuckAccount, AccountMixin, accountClass, + getSortedAccounts +) +from tr import _translate +from retranslateui import RetranslateMixin +import widgets + +import hashlib +from inventory import Inventory + + +class AddressCheckMixin(object): + + def __init__(self): + self.valid = False + QtCore.QObject.connect(self.lineEditAddress, QtCore.SIGNAL( + "textChanged(QString)"), self.addressChanged) + + def _onSuccess(self, addressVersion, streamNumber, ripe): + pass + + def addressChanged(self, QString): + status, addressVersion, streamNumber, ripe = decodeAddress( + str(QString)) + self.valid = status == 'success' + if self.valid: + self.labelAddressCheck.setText( + _translate("MainWindow", "Address is valid.")) + self._onSuccess(addressVersion, streamNumber, ripe) + elif status == 'missingbm': + self.labelAddressCheck.setText(_translate( + "MainWindow", "The address should start with ''BM-''")) + elif status == 'checksumfailed': + self.labelAddressCheck.setText(_translate( + "MainWindow", + "The address is not typed or copied correctly" + " (the checksum failed)." + )) + elif status == 'versiontoohigh': + self.labelAddressCheck.setText(_translate( + "MainWindow", + "The version number of this address is higher than this" + " software can support. Please upgrade Bitmessage." + )) + elif status == 'invalidcharacters': + self.labelAddressCheck.setText(_translate( + "MainWindow", "The address contains invalid characters.")) + elif status == 'ripetooshort': + self.labelAddressCheck.setText(_translate( + "MainWindow", + "Some data encoded in the address is too short." + )) + elif status == 'ripetoolong': + self.labelAddressCheck.setText(_translate( + "MainWindow", "Some data encoded in the address is too long.")) + elif status == 'varintmalformed': + self.labelAddressCheck.setText(_translate( + "MainWindow", + "Some data encoded in the address is malformed." + )) + + +class AddAddressDialog(QtGui.QDialog, RetranslateMixin, AddressCheckMixin): + + def __init__(self, parent=None): + super(AddAddressDialog, self).__init__(parent) + widgets.load('addaddressdialog.ui', self) + AddressCheckMixin.__init__(self) + + +class NewAddressDialog(QtGui.QDialog, RetranslateMixin): + + def __init__(self, parent=None): + super(NewAddressDialog, self).__init__(parent) + widgets.load('newaddressdialog.ui', self) + + # Let's fill out the 'existing address' combo box with addresses + # from the 'Your Identities' tab. + for address in getSortedAccounts(): + self.radioButtonExisting.click() + self.comboBoxExisting.addItem(address) + self.groupBoxDeterministic.setHidden(True) + QtGui.QWidget.resize(self, QtGui.QWidget.sizeHint(self)) + + +class NewSubscriptionDialog( + QtGui.QDialog, RetranslateMixin, AddressCheckMixin): + + def __init__(self, parent=None): + super(NewSubscriptionDialog, self).__init__(parent) + widgets.load('newsubscriptiondialog.ui', self) + AddressCheckMixin.__init__(self) + + def _onSuccess(self, addressVersion, streamNumber, ripe): + if addressVersion <= 3: + self.checkBoxDisplayMessagesAlreadyInInventory.setText(_translate( + "MainWindow", + "Address is an old type. We cannot display its past" + " broadcasts." + )) + else: + Inventory().flush() + doubleHashOfAddressData = hashlib.sha512(hashlib.sha512( + encodeVarint(addressVersion) + + encodeVarint(streamNumber) + ripe + ).digest()).digest() + tag = doubleHashOfAddressData[32:] + self.recent = Inventory().by_type_and_tag(3, tag) + count = len(self.recent) + if count == 0: + self.checkBoxDisplayMessagesAlreadyInInventory.setText( + _translate( + "MainWindow", + "There are no recent broadcasts from this address" + " to display." + )) + else: + self.checkBoxDisplayMessagesAlreadyInInventory.setEnabled(True) + self.checkBoxDisplayMessagesAlreadyInInventory.setText( + _translate( + "MainWindow", + "Display the %1 recent broadcast(s) from this address." + ).arg(count)) + + +class RegenerateAddressesDialog(QtGui.QDialog, RetranslateMixin): + def __init__(self, parent=None): + super(RegenerateAddressesDialog, self).__init__(parent) + widgets.load('regenerateaddresses.ui', self) + QtGui.QWidget.resize(self, QtGui.QWidget.sizeHint(self)) + + +class SpecialAddressBehaviorDialog(QtGui.QDialog, RetranslateMixin): + + def __init__(self, parent=None, config=None): + super(SpecialAddressBehaviorDialog, self).__init__(parent) + widgets.load('specialaddressbehavior.ui', self) + self.address = parent.getCurrentAccount() + self.parent = parent + self.config = config + + try: + self.address_is_chan = config.safeGetBoolean( + self.address, 'chan' + ) + except AttributeError: + pass + else: + if self.address_is_chan: # address is a chan address + self.radioButtonBehaviorMailingList.setDisabled(True) + self.lineEditMailingListName.setText(_translate( + "MainWindow", + "This is a chan address. You cannot use it as a" + " pseudo-mailing list." + )) + else: + if config.safeGetBoolean(self.address, 'mailinglist'): + self.radioButtonBehaviorMailingList.click() + else: + self.radioButtonBehaveNormalAddress.click() + try: + mailingListName = config.get( + self.address, 'mailinglistname') + except: + mailingListName = '' + self.lineEditMailingListName.setText( + unicode(mailingListName, 'utf-8') + ) + + QtGui.QWidget.resize(self, QtGui.QWidget.sizeHint(self)) + self.show() + + def accept(self): + self.hide() + if self.address_is_chan: + return + if self.radioButtonBehaveNormalAddress.isChecked(): + self.config.set(str(self.address), 'mailinglist', 'false') + # Set the color to either black or grey + if self.config.getboolean(self.address, 'enabled'): + self.parent.setCurrentItemColor( + QtGui.QApplication.palette().text().color() + ) + else: + self.parent.setCurrentItemColor(QtGui.QColor(128, 128, 128)) + else: + self.config.set(str(self.address), 'mailinglist', 'true') + self.config.set(str(self.address), 'mailinglistname', str( + self.lineEditMailingListName.text().toUtf8())) + self.parent.setCurrentItemColor( + QtGui.QColor(137, 04, 177)) # magenta + self.parent.rerenderComboBoxSendFrom() + self.parent.rerenderComboBoxSendFromBroadcast() + self.config.save() + self.parent.rerenderMessagelistToLabels() + + +class EmailGatewayDialog(QtGui.QDialog, RetranslateMixin): + def __init__(self, parent, title=None, label=None, config=None): + super(EmailGatewayDialog, self).__init__(parent) + widgets.load('emailgateway.ui', self) + self.parent = parent + self.config = config + if title and label: + self.setWindowTitle(title) + self.label.setText(label) + self.radioButtonRegister.hide() + self.radioButtonStatus.hide() + self.radioButtonSettings.hide() + self.radioButtonUnregister.hide() + else: + address = parent.getCurrentAccount() + self.acct = accountClass(address) + try: + label = config.get(address, 'label') + except AttributeError: + pass + else: + if "@" in label: + self.lineEditEmail.setText(label) + if isinstance(self.acct, GatewayAccount): + self.radioButtonUnregister.setEnabled(True) + self.radioButtonStatus.setEnabled(True) + self.radioButtonStatus.setChecked(True) + self.radioButtonSettings.setEnabled(True) + self.lineEditEmail.setEnabled(False) + else: + self.acct = MailchuckAccount(address) + self.lineEditEmail.setFocus() + QtGui.QWidget.resize(self, QtGui.QWidget.sizeHint(self)) + + def register(self, acct=None): + email = str(self.lineEditEmail.text().toUtf8()) + if acct is None: + acct = self.acct + acct.register(email) + self.config.set(acct.fromAddress, 'label', email) + self.config.set(acct.fromAddress, 'gateway', 'mailchuck') + self.config.save() + self.parent.statusBar().showMessage(_translate( + "MainWindow", + "Sending email gateway registration request" + ), 10000) + + def accept(self): + self.hide() + # no chans / mailinglists + if self.acct.type != AccountMixin.NORMAL: + return + + if not isinstance(self.acct, GatewayAccount): + return + + if self.radioButtonRegister.isChecked(): + self.register() + elif self.radioButtonUnregister.isChecked(): + self.acct.unregister() + self.config.remove_option(self.acct.fromAddress, 'gateway') + self.config.save() + self.parent.statusBar().showMessage(_translate( + "MainWindow", + "Sending email gateway unregistration request" + ), 10000) + elif self.radioButtonStatus.isChecked(): + self.acct.status() + self.parent.statusBar().showMessage(_translate( + "MainWindow", + "Sending email gateway status request" + ), 10000) + elif self.radioButtonSettings.isChecked(): + return self.acct diff --git a/src/bitmessageqt/dialogs.py b/src/bitmessageqt/dialogs.py index c7cd5bd4..80bffed4 100644 --- a/src/bitmessageqt/dialogs.py +++ b/src/bitmessageqt/dialogs.py @@ -1,143 +1,23 @@ -from PyQt4 import QtCore, QtGui -from addresses import decodeAddress, encodeVarint -from account import ( - GatewayAccount, MailchuckAccount, AccountMixin, accountClass, - getSortedAccounts -) +from PyQt4 import QtGui from tr import _translate from retranslateui import RetranslateMixin import widgets from newchandialog import NewChanDialog +from address_dialogs import ( + AddAddressDialog, NewAddressDialog, NewSubscriptionDialog, + RegenerateAddressesDialog, SpecialAddressBehaviorDialog, EmailGatewayDialog +) -import hashlib import paths -from inventory import Inventory from version import softwareVersion -__all__ = [NewChanDialog] - - -class AddressCheckMixin(object): - - def __init__(self): - self.valid = False - QtCore.QObject.connect(self.lineEditAddress, QtCore.SIGNAL( - "textChanged(QString)"), self.addressChanged) - - def _onSuccess(self, addressVersion, streamNumber, ripe): - pass - - def addressChanged(self, QString): - status, addressVersion, streamNumber, ripe = decodeAddress( - str(QString)) - self.valid = status == 'success' - if self.valid: - self.labelAddressCheck.setText( - _translate("MainWindow", "Address is valid.")) - self._onSuccess(addressVersion, streamNumber, ripe) - elif status == 'missingbm': - self.labelAddressCheck.setText(_translate( - "MainWindow", "The address should start with ''BM-''")) - elif status == 'checksumfailed': - self.labelAddressCheck.setText(_translate( - "MainWindow", - "The address is not typed or copied correctly" - " (the checksum failed)." - )) - elif status == 'versiontoohigh': - self.labelAddressCheck.setText(_translate( - "MainWindow", - "The version number of this address is higher than this" - " software can support. Please upgrade Bitmessage." - )) - elif status == 'invalidcharacters': - self.labelAddressCheck.setText(_translate( - "MainWindow", "The address contains invalid characters.")) - elif status == 'ripetooshort': - self.labelAddressCheck.setText(_translate( - "MainWindow", - "Some data encoded in the address is too short." - )) - elif status == 'ripetoolong': - self.labelAddressCheck.setText(_translate( - "MainWindow", "Some data encoded in the address is too long.")) - elif status == 'varintmalformed': - self.labelAddressCheck.setText(_translate( - "MainWindow", - "Some data encoded in the address is malformed." - )) - - -class AddAddressDialog(QtGui.QDialog, RetranslateMixin, AddressCheckMixin): - - def __init__(self, parent=None): - super(AddAddressDialog, self).__init__(parent) - widgets.load('addaddressdialog.ui', self) - AddressCheckMixin.__init__(self) - - -class NewAddressDialog(QtGui.QDialog, RetranslateMixin): - - def __init__(self, parent=None): - super(NewAddressDialog, self).__init__(parent) - widgets.load('newaddressdialog.ui', self) - - # Let's fill out the 'existing address' combo box with addresses - # from the 'Your Identities' tab. - for address in getSortedAccounts(): - self.radioButtonExisting.click() - self.comboBoxExisting.addItem(address) - self.groupBoxDeterministic.setHidden(True) - QtGui.QWidget.resize(self, QtGui.QWidget.sizeHint(self)) - - -class NewSubscriptionDialog( - QtGui.QDialog, RetranslateMixin, AddressCheckMixin): - - def __init__(self, parent=None): - super(NewSubscriptionDialog, self).__init__(parent) - widgets.load('newsubscriptiondialog.ui', self) - AddressCheckMixin.__init__(self) - - def _onSuccess(self, addressVersion, streamNumber, ripe): - if addressVersion <= 3: - self.checkBoxDisplayMessagesAlreadyInInventory.setText(_translate( - "MainWindow", - "Address is an old type. We cannot display its past" - " broadcasts." - )) - else: - Inventory().flush() - doubleHashOfAddressData = hashlib.sha512(hashlib.sha512( - encodeVarint(addressVersion) + - encodeVarint(streamNumber) + ripe - ).digest()).digest() - tag = doubleHashOfAddressData[32:] - self.recent = Inventory().by_type_and_tag(3, tag) - count = len(self.recent) - if count == 0: - self.checkBoxDisplayMessagesAlreadyInInventory.setText( - _translate( - "MainWindow", - "There are no recent broadcasts from this address" - " to display." - )) - else: - self.checkBoxDisplayMessagesAlreadyInInventory.setEnabled(True) - self.checkBoxDisplayMessagesAlreadyInInventory.setText( - _translate( - "MainWindow", - "Display the %1 recent broadcast(s) from this address." - ).arg(count)) - - -class RegenerateAddressesDialog(QtGui.QDialog, RetranslateMixin): - def __init__(self, parent=None): - super(RegenerateAddressesDialog, self).__init__(parent) - widgets.load('regenerateaddresses.ui', self) - QtGui.QWidget.resize(self, QtGui.QWidget.sizeHint(self)) +__all__ = [ + "NewChanDialog", "AddAddressDialog", "NewAddressDialog", + "NewSubscriptionDialog", "RegenerateAddressesDialog", + "SpecialAddressBehaviorDialog", "EmailGatewayDialog" +] class AboutDialog(QtGui.QDialog, RetranslateMixin): @@ -194,144 +74,3 @@ class ConnectDialog(QtGui.QDialog, RetranslateMixin): super(ConnectDialog, self).__init__(parent) widgets.load('connect.ui', self) QtGui.QWidget.resize(self, QtGui.QWidget.sizeHint(self)) - - -class SpecialAddressBehaviorDialog(QtGui.QDialog, RetranslateMixin): - - def __init__(self, parent=None, config=None): - super(SpecialAddressBehaviorDialog, self).__init__(parent) - widgets.load('specialaddressbehavior.ui', self) - self.address = parent.getCurrentAccount() - self.parent = parent - self.config = config - - try: - self.address_is_chan = config.safeGetBoolean( - self.address, 'chan' - ) - except AttributeError: - pass - else: - if self.address_is_chan: # address is a chan address - self.radioButtonBehaviorMailingList.setDisabled(True) - self.lineEditMailingListName.setText(_translate( - "MainWindow", - "This is a chan address. You cannot use it as a" - " pseudo-mailing list." - )) - else: - if config.safeGetBoolean(self.address, 'mailinglist'): - self.radioButtonBehaviorMailingList.click() - else: - self.radioButtonBehaveNormalAddress.click() - try: - mailingListName = config.get( - self.address, 'mailinglistname') - except: - mailingListName = '' - self.lineEditMailingListName.setText( - unicode(mailingListName, 'utf-8') - ) - - QtGui.QWidget.resize(self, QtGui.QWidget.sizeHint(self)) - self.show() - - def accept(self): - self.hide() - if self.address_is_chan: - return - if self.radioButtonBehaveNormalAddress.isChecked(): - self.config.set(str(self.address), 'mailinglist', 'false') - # Set the color to either black or grey - if self.config.getboolean(self.address, 'enabled'): - self.parent.setCurrentItemColor( - QtGui.QApplication.palette().text().color() - ) - else: - self.parent.setCurrentItemColor(QtGui.QColor(128, 128, 128)) - else: - self.config.set(str(self.address), 'mailinglist', 'true') - self.config.set(str(self.address), 'mailinglistname', str( - self.lineEditMailingListName.text().toUtf8())) - self.parent.setCurrentItemColor( - QtGui.QColor(137, 04, 177)) # magenta - self.parent.rerenderComboBoxSendFrom() - self.parent.rerenderComboBoxSendFromBroadcast() - self.config.save() - self.parent.rerenderMessagelistToLabels() - - -class EmailGatewayDialog(QtGui.QDialog, RetranslateMixin): - def __init__(self, parent, title=None, label=None, config=None): - super(EmailGatewayDialog, self).__init__(parent) - widgets.load('emailgateway.ui', self) - self.parent = parent - self.config = config - if title and label: - self.setWindowTitle(title) - self.label.setText(label) - self.radioButtonRegister.hide() - self.radioButtonStatus.hide() - self.radioButtonSettings.hide() - self.radioButtonUnregister.hide() - else: - address = parent.getCurrentAccount() - self.acct = accountClass(address) - try: - label = config.get(address, 'label') - except AttributeError: - pass - else: - if "@" in label: - self.lineEditEmail.setText(label) - if isinstance(self.acct, GatewayAccount): - self.radioButtonUnregister.setEnabled(True) - self.radioButtonStatus.setEnabled(True) - self.radioButtonStatus.setChecked(True) - self.radioButtonSettings.setEnabled(True) - self.lineEditEmail.setEnabled(False) - else: - self.acct = MailchuckAccount(address) - self.lineEditEmail.setFocus() - QtGui.QWidget.resize(self, QtGui.QWidget.sizeHint(self)) - - def register(self, acct=None): - email = str(self.lineEditEmail.text().toUtf8()) - if acct is None: - acct = self.acct - acct.register(email) - self.config.set(acct.fromAddress, 'label', email) - self.config.set(acct.fromAddress, 'gateway', 'mailchuck') - self.config.save() - self.parent.statusBar().showMessage(_translate( - "MainWindow", - "Sending email gateway registration request" - ), 10000) - - def accept(self): - self.hide() - # no chans / mailinglists - if self.acct.type != AccountMixin.NORMAL: - return - - if not isinstance(self.acct, GatewayAccount): - return - - if self.radioButtonRegister.isChecked(): - self.register() - elif self.radioButtonUnregister.isChecked(): - self.acct.unregister() - self.config.remove_option(self.acct.fromAddress, 'gateway') - self.config.save() - self.parent.statusBar().showMessage(_translate( - "MainWindow", - "Sending email gateway unregistration request" - ), 10000) - elif self.radioButtonStatus.isChecked(): - self.acct.status() - self.parent.statusBar().showMessage(_translate( - "MainWindow", - "Sending email gateway status request" - ), 10000) - elif self.radioButtonSettings.isChecked(): - return self.acct From 41155406d62bc767bd44169dae02d9e406586eb6 Mon Sep 17 00:00:00 2001 From: Dmitri Bogomolov <4glitch@gmail.com> Date: Tue, 23 Jan 2018 18:15:11 +0200 Subject: [PATCH 349/407] Simplified showing statusbar messages: - Use MyForm.statusbar set in __init__ instead of MyForm.statusBar() - MyForm.updateStatusBar() to show the message in MyForm methods - queues.UISignalQueue.put(('updateStatusBar', msg)) in dialogs --- src/bitmessageqt/__init__.py | 338 ++++++++++++++++++---------- src/bitmessageqt/address_dialogs.py | 33 +-- 2 files changed, 237 insertions(+), 134 deletions(-) diff --git a/src/bitmessageqt/__init__.py b/src/bitmessageqt/__init__.py index dfcb9685..ba4e3fd0 100644 --- a/src/bitmessageqt/__init__.py +++ b/src/bitmessageqt/__init__.py @@ -1522,7 +1522,6 @@ class MyForm(settingsmixin.SMainWindow): pass # QtGui.QWidget.changeEvent(self, event) - def __icon_activated(self, reason): if reason == QtGui.QSystemTrayIcon.Trigger: self.actionShow.setChecked(not self.actionShow.isChecked()) @@ -1547,8 +1546,12 @@ class MyForm(settingsmixin.SMainWindow): sound.SOUND_DISCONNECTED) if not BMConfigParser().safeGetBoolean('bitmessagesettings', 'upnp') and \ BMConfigParser().get('bitmessagesettings', 'socksproxytype') == "none": - self.statusBar().showMessage(_translate( - "MainWindow", "Problems connecting? Try enabling UPnP in the Network Settings"), 10000) + self.updateStatusBar( + _translate( + "MainWindow", + "Problems connecting? Try enabling UPnP in the Network" + " Settings" + )) self.connected = False if self.actionStatus is not None: @@ -1556,8 +1559,8 @@ class MyForm(settingsmixin.SMainWindow): "MainWindow", "Not Connected")) self.setTrayIconFile("can-icon-24px-red.png") if color == 'yellow': - if self.statusBar().currentMessage() == 'Warning: You are currently not connected. Bitmessage will do the work necessary to send the message but it won\'t send until you connect.': - self.statusBar().clearMessage() + if self.statusbar.currentMessage() == 'Warning: You are currently not connected. Bitmessage will do the work necessary to send the message but it won\'t send until you connect.': + self.statusbar.clearMessage() self.pushButtonStatusIcon.setIcon( QtGui.QIcon(":/newPrefix/images/yellowicon.png")) shared.statusIconColor = 'yellow' @@ -1574,8 +1577,8 @@ class MyForm(settingsmixin.SMainWindow): "MainWindow", "Connected")) self.setTrayIconFile("can-icon-24px-yellow.png") if color == 'green': - if self.statusBar().currentMessage() == 'Warning: You are currently not connected. Bitmessage will do the work necessary to send the message but it won\'t send until you connect.': - self.statusBar().clearMessage() + if self.statusbar.currentMessage() == 'Warning: You are currently not connected. Bitmessage will do the work necessary to send the message but it won\'t send until you connect.': + self.statusbar.clearMessage() self.pushButtonStatusIcon.setIcon( QtGui.QIcon(":/newPrefix/images/greenicon.png")) shared.statusIconColor = 'green' @@ -1705,19 +1708,24 @@ class MyForm(settingsmixin.SMainWindow): self.ui.tableWidgetInboxChans]): for i in range(inbox.rowCount()): if msgid == str(inbox.item(i, 3).data(QtCore.Qt.UserRole).toPyObject()): - self.statusBar().showMessage(_translate( - "MainWindow", "Message trashed"), 10000) + self.updateStatusBar( + _translate("MainWindow", "Message trashed")) treeWidget = self.widgetConvert(inbox) self.propagateUnreadCount(inbox.item(i, 1 if inbox.item(i, 1).type == AccountMixin.SUBSCRIPTION else 0).data(QtCore.Qt.UserRole), self.getCurrentFolder(treeWidget), treeWidget, 0) inbox.removeRow(i) break - + def newVersionAvailable(self, version): self.notifiedNewVersion = ".".join(str(n) for n in version) - self.statusBar().showMessage(_translate("MainWindow", "New version of PyBitmessage is available: %1. Download it from https://github.com/Bitmessage/PyBitmessage/releases/latest").arg(self.notifiedNewVersion), 10000) + self.updateStatusBar(_translate( + "MainWindow", + "New version of PyBitmessage is available: %1. Download it" + " from https://github.com/Bitmessage/PyBitmessage/releases/latest" + ).arg(self.notifiedNewVersion) + ) def displayAlert(self, title, text, exitAfterUserClicksOk): - self.statusBar().showMessage(text) + self.updateStatusBar(text) QtGui.QMessageBox.critical(self, title, text, QtGui.QMessageBox.Ok) if exitAfterUserClicksOk: os._exit(0) @@ -1799,7 +1807,7 @@ class MyForm(settingsmixin.SMainWindow): def click_pushButtonSend(self): encoding = 3 if QtGui.QApplication.queryKeyboardModifiers() & QtCore.Qt.ShiftModifier else 2 - self.statusBar().clearMessage() + self.statusbar.clearMessage() if self.ui.tabWidgetSend.currentIndex() == \ self.ui.tabWidgetSend.indexOf(self.ui.sendDirect): @@ -1871,8 +1879,14 @@ class MyForm(settingsmixin.SMainWindow): BMConfigParser().set(fromAddress, 'label', email) BMConfigParser().set(fromAddress, 'gateway', 'mailchuck') BMConfigParser().save() - self.statusBar().showMessage(_translate( - "MainWindow", "Error: Your account wasn't registered at an email gateway. Sending registration now as %1, please wait for the registration to be processed before retrying sending.").arg(email), 10000) + self.updateStatusBar(_translate( + "MainWindow", + "Error: Your account wasn't registered at" + " an email gateway. Sending registration" + " now as %1, please wait for the registration" + " to be processed before retrying sending." + ).arg(email) + ) return status, addressVersionNumber, streamNumber, ripe = decodeAddress( toAddress) @@ -1884,32 +1898,68 @@ class MyForm(settingsmixin.SMainWindow): logger.error('Error: Could not decode recipient address ' + toAddress + ':' + status) if status == 'missingbm': - self.statusBar().showMessage(_translate( - "MainWindow", "Error: Bitmessage addresses start with BM- Please check the recipient address %1").arg(toAddress), 10000) + self.updateStatusBar(_translate( + "MainWindow", + "Error: Bitmessage addresses start with" + " BM- Please check the recipient address %1" + ).arg(toAddress)) elif status == 'checksumfailed': - self.statusBar().showMessage(_translate( - "MainWindow", "Error: The recipient address %1 is not typed or copied correctly. Please check it.").arg(toAddress), 10000) + self.statusbar_message(_translate( + "MainWindow", + "Error: The recipient address %1 is not" + " typed or copied correctly. Please check it." + ).arg(toAddress)) elif status == 'invalidcharacters': - self.statusBar().showMessage(_translate( - "MainWindow", "Error: The recipient address %1 contains invalid characters. Please check it.").arg(toAddress), 10000) + self.updateStatusBar(_translate( + "MainWindow", + "Error: The recipient address %1 contains" + " invalid characters. Please check it." + ).arg(toAddress)) elif status == 'versiontoohigh': - self.statusBar().showMessage(_translate( - "MainWindow", "Error: The version of the recipient address %1 is too high. Either you need to upgrade your Bitmessage software or your acquaintance is being clever.").arg(toAddress), 10000) + self.updateStatusBar(_translate( + "MainWindow", + "Error: The version of the recipient address" + " %1 is too high. Either you need to upgrade" + " your Bitmessage software or your" + " acquaintance is being clever." + ).arg(toAddress)) elif status == 'ripetooshort': - self.statusBar().showMessage(_translate( - "MainWindow", "Error: Some data encoded in the recipient address %1 is too short. There might be something wrong with the software of your acquaintance.").arg(toAddress), 10000) + self.updateStatusBar(_translate( + "MainWindow", + "Error: Some data encoded in the recipient" + " address %1 is too short. There might be" + " something wrong with the software of" + " your acquaintance." + ).arg(toAddress)) elif status == 'ripetoolong': - self.statusBar().showMessage(_translate( - "MainWindow", "Error: Some data encoded in the recipient address %1 is too long. There might be something wrong with the software of your acquaintance.").arg(toAddress), 10000) + self.updateStatusBar(_translate( + "MainWindow", + "Error: Some data encoded in the recipient" + " address %1 is too long. There might be" + " something wrong with the software of" + " your acquaintance." + ).arg(toAddress)) elif status == 'varintmalformed': - self.statusBar().showMessage(_translate( - "MainWindow", "Error: Some data encoded in the recipient address %1 is malformed. There might be something wrong with the software of your acquaintance.").arg(toAddress), 10000) + self.updateStatusBar(_translate( + "MainWindow", + "Error: Some data encoded in the recipient" + " address %1 is malformed. There might be" + " something wrong with the software of" + " your acquaintance." + ).arg(toAddress)) else: - self.statusBar().showMessage(_translate( - "MainWindow", "Error: Something is wrong with the recipient address %1.").arg(toAddress), 10000) + self.updateStatusBar(_translate( + "MainWindow", + "Error: Something is wrong with the" + " recipient address %1." + ).arg(toAddress)) elif fromAddress == '': - self.statusBar().showMessage(_translate( - "MainWindow", "Error: You must specify a From address. If you don\'t have one, go to the \'Your Identities\' tab."), 10000) + self.updateStatusBar(_translate( + "MainWindow", + "Error: You must specify a From address. If you" + " don\'t have one, go to the" + " \'Your Identities\' tab.") + ) else: toAddress = addBMIfNotPresent(toAddress) @@ -1921,11 +1971,17 @@ class MyForm(settingsmixin.SMainWindow): QtGui.QMessageBox.about(self, _translate("MainWindow", "Stream number"), _translate( "MainWindow", "Concerning the address %1, Bitmessage cannot handle stream numbers of %2. Perhaps upgrade Bitmessage to the latest version.").arg(toAddress).arg(str(streamNumber))) continue - self.statusBar().clearMessage() + self.statusbar.clearMessage() if shared.statusIconColor == 'red': - self.statusBar().showMessage(_translate( - "MainWindow", "Warning: You are currently not connected. Bitmessage will do the work necessary to send the message but it won\'t send until you connect.")) - stealthLevel = BMConfigParser().safeGetInt('bitmessagesettings', 'ackstealthlevel') + self.updateStatusBar(_translate( + "MainWindow", + "Warning: You are currently not connected." + " Bitmessage will do the work necessary to" + " send the message but it won\'t send until" + " you connect.") + ) + stealthLevel = BMConfigParser().safeGetInt( + 'bitmessagesettings', 'ackstealthlevel') ackdata = genAckPayload(streamNumber, stealthLevel) t = () sqlExecute( @@ -1965,18 +2021,21 @@ class MyForm(settingsmixin.SMainWindow): if self.replyFromTab is not None: self.ui.tabWidget.setCurrentIndex(self.replyFromTab) self.replyFromTab = None - self.statusBar().showMessage(_translate( - "MainWindow", "Message queued."), 10000) - #self.ui.tableWidgetInbox.setCurrentCell(0, 0) + self.updateStatusBar(_translate( + "MainWindow", "Message queued.")) + # self.ui.tableWidgetInbox.setCurrentCell(0, 0) else: - self.statusBar().showMessage(_translate( - "MainWindow", "Your \'To\' field is empty."), 10000) + self.updateStatusBar(_translate( + "MainWindow", "Your \'To\' field is empty.")) else: # User selected 'Broadcast' if fromAddress == '': - self.statusBar().showMessage(_translate( - "MainWindow", "Error: You must specify a From address. If you don\'t have one, go to the \'Your Identities\' tab."), 10000) + self.updateStatusBar(_translate( + "MainWindow", + "Error: You must specify a From address. If you don\'t" + " have one, go to the \'Your Identities\' tab." + )) else: - self.statusBar().clearMessage() + self.statusbar.clearMessage() # We don't actually need the ackdata for acknowledgement since # this is a broadcast message, but we can use it to update the # user interface when the POW is done generating. @@ -2016,30 +2075,32 @@ class MyForm(settingsmixin.SMainWindow): self.ui.tabWidget.indexOf(self.ui.send) ) self.ui.tableWidgetInboxSubscriptions.setCurrentCell(0, 0) - self.statusBar().showMessage(_translate( - "MainWindow", "Broadcast queued."), 10000) + self.updateStatusBar(_translate( + "MainWindow", "Broadcast queued.")) def click_pushButtonLoadFromAddressBook(self): self.ui.tabWidget.setCurrentIndex(5) for i in range(4): time.sleep(0.1) - self.statusBar().clearMessage() + self.statusbar.clearMessage() time.sleep(0.1) - self.statusBar().showMessage(_translate( - "MainWindow", "Right click one or more entries in your address book and select \'Send message to this address\'."), 10000) + self.updateStatusBar(_translate( + "MainWindow", + "Right click one or more entries in your address book and" + " select \'Send message to this address\'." + )) def click_pushButtonFetchNamecoinID(self): nc = namecoinConnection() identities = str(self.ui.lineEditTo.text().toUtf8()).split(";") err, addr = nc.query(identities[-1].strip()) if err is not None: - self.statusBar().showMessage(_translate( - "MainWindow", "Error: %1").arg(err), 10000) + self.statusbar_message("Error: %1", args=[err]) else: identities[-1] = addr self.ui.lineEditTo.setText("; ".join(identities)) - self.statusBar().showMessage(_translate( - "MainWindow", "Fetched address from namecoin identity."), 10000) + self.updateStatusBar(_translate( + "MainWindow", "Fetched address from namecoin identity.")) def setBroadcastEnablementDependingOnWhetherThisIsAMailingListAddress(self, address): # If this is a chan then don't let people broadcast because no one @@ -2183,23 +2244,22 @@ class MyForm(settingsmixin.SMainWindow): dialog = dialogs.AddAddressDialog(self) if dialog.exec_(): if not dialog.valid: - self.statusBar().showMessage(_translate( + self.updateStatusBar(_translate( "MainWindow", "The address you entered was invalid. Ignoring it." - ), 10000) + )) return - address = addBMIfNotPresent(str(dialog.lineEditAddress.text())) # First we must check to see if the address is already in the # address book. The user cannot add it again or else it will # cause problems when updating and deleting the entry. if shared.isAddressInMyAddressBook(address): - self.statusBar().showMessage(_translate( + self.updateStatusBar(_translate( "MainWindow", "Error: You cannot add the same address to your" " address book twice. Try renaming the existing one" " if you want." - ), 10000) + )) return label = str(dialog.lineEditLabel.text().toUtf8()) self.addEntryToAddressBook(address, label) @@ -2231,23 +2291,22 @@ class MyForm(settingsmixin.SMainWindow): dialog = dialogs.NewSubscriptionDialog(self) if dialog.exec_(): if not dialog.valid: - self.statusBar().showMessage(_translate( + self.updateStatusBar(_translate( "MainWindow", "The address you entered was invalid. Ignoring it." - ), 10000) + )) return - address = addBMIfNotPresent(str(dialog.lineEditAddress.text())) # We must check to see if the address is already in the # subscriptions list. The user cannot add it again or else it # will cause problems when updating and deleting the entry. if shared.isAddressInMySubscriptionsList(address): - self.statusBar().showMessage(_translate( + self.updateStatusBar(_translate( "MainWindow", "Error: You cannot add the same address to your" " subscriptions twice. Perhaps rename the existing one" " if you want." - ), 10000) + )) return label = str(dialog.lineEditLabel.text().toUtf8()) self.addSubscription(address, label) @@ -2322,7 +2381,7 @@ class MyForm(settingsmixin.SMainWindow): QtGui.QMessageBox.about(self, _translate("MainWindow", "Restart"), _translate( "MainWindow", "Bitmessage will use your proxy from now on but you may want to manually restart Bitmessage now to close existing connections (if any).")) if BMConfigParser().get('bitmessagesettings', 'socksproxytype')[0:5] == 'SOCKS' and self.settingsDialogInstance.ui.comboBoxProxyType.currentText()[0:5] != 'SOCKS': - self.statusBar().clearMessage() + self.statusbar.clearMessage() state.resetNetworkProtocolAvailability() # just in case we changed something in the network connectivity if self.settingsDialogInstance.ui.comboBoxProxyType.currentText()[0:5] == 'SOCKS': BMConfigParser().set('bitmessagesettings', 'socksproxytype', str( @@ -2674,23 +2733,27 @@ class MyForm(settingsmixin.SMainWindow): self.quitAccepted = True - self.statusBar().showMessage(_translate( - "MainWindow", "Shutting down PyBitmessage... %1%").arg(str(0))) + self.updateStatusBar(_translate( + "MainWindow", "Shutting down PyBitmessage... %1%").arg(0)) if waitForConnection: - self.statusBar().showMessage(_translate( + self.updateStatusBar(_translate( "MainWindow", "Waiting for network connection...")) while shared.statusIconColor == 'red': time.sleep(0.5) - QtCore.QCoreApplication.processEvents(QtCore.QEventLoop.AllEvents, 1000) + QtCore.QCoreApplication.processEvents( + QtCore.QEventLoop.AllEvents, 1000 + ) # this probably will not work correctly, because there is a delay between the status icon turning red and inventory exchange, but it's better than nothing. if waitForSync: - self.statusBar().showMessage(_translate( + self.updateStatusBar(_translate( "MainWindow", "Waiting for finishing synchronisation...")) while PendingDownloadQueue.totalSize() > 0: time.sleep(0.5) - QtCore.QCoreApplication.processEvents(QtCore.QEventLoop.AllEvents, 1000) + QtCore.QCoreApplication.processEvents( + QtCore.QEventLoop.AllEvents, 1000 + ) if waitForPow: # check if PoW queue empty @@ -2702,51 +2765,83 @@ class MyForm(settingsmixin.SMainWindow): if curWorkerQueue > maxWorkerQueue: maxWorkerQueue = curWorkerQueue if curWorkerQueue > 0: - self.statusBar().showMessage(_translate("MainWindow", "Waiting for PoW to finish... %1%").arg(str(50 * (maxWorkerQueue - curWorkerQueue) / maxWorkerQueue))) + self.updateStatusBar(_translate( + "MainWindow", "Waiting for PoW to finish... %1%" + ).arg(50 * (maxWorkerQueue - curWorkerQueue) + / maxWorkerQueue) + ) time.sleep(0.5) - QtCore.QCoreApplication.processEvents(QtCore.QEventLoop.AllEvents, 1000) - - self.statusBar().showMessage(_translate("MainWindow", "Shutting down Pybitmessage... %1%").arg(str(50))) - - QtCore.QCoreApplication.processEvents(QtCore.QEventLoop.AllEvents, 1000) + QtCore.QCoreApplication.processEvents( + QtCore.QEventLoop.AllEvents, 1000 + ) + + self.updateStatusBar(_translate( + "MainWindow", "Shutting down Pybitmessage... %1%").arg(50)) + + QtCore.QCoreApplication.processEvents( + QtCore.QEventLoop.AllEvents, 1000 + ) if maxWorkerQueue > 0: - time.sleep(0.5) # a bit of time so that the hashHolder is populated - QtCore.QCoreApplication.processEvents(QtCore.QEventLoop.AllEvents, 1000) - + # a bit of time so that the hashHolder is populated + time.sleep(0.5) + QtCore.QCoreApplication.processEvents( + QtCore.QEventLoop.AllEvents, 1000 + ) + # check if upload (of objects created locally) pending - self.statusBar().showMessage(_translate("MainWindow", "Waiting for objects to be sent... %1%").arg(str(50))) + self.updateStatusBar(_translate( + "MainWindow", "Waiting for objects to be sent... %1%").arg(50)) try: while PendingUpload().progress() < 1: - self.statusBar().showMessage(_translate("MainWindow", "Waiting for objects to be sent... %1%").arg(str(int(50 + 20 * PendingUpload().progress())))) + self.updateStatusBar(_translate( + "MainWindow", + "Waiting for objects to be sent... %1%" + ).arg(int(50 + 20 * PendingUpload().progress())) + ) time.sleep(0.5) - QtCore.QCoreApplication.processEvents(QtCore.QEventLoop.AllEvents, 1000) + QtCore.QCoreApplication.processEvents( + QtCore.QEventLoop.AllEvents, 1000 + ) except PendingUploadDeadlineException: pass - QtCore.QCoreApplication.processEvents(QtCore.QEventLoop.AllEvents, 1000) - QtCore.QCoreApplication.processEvents(QtCore.QEventLoop.AllEvents, 1000) + QtCore.QCoreApplication.processEvents( + QtCore.QEventLoop.AllEvents, 1000 + ) + QtCore.QCoreApplication.processEvents( + QtCore.QEventLoop.AllEvents, 1000 + ) # save state and geometry self and all widgets - self.statusBar().showMessage(_translate("MainWindow", "Saving settings... %1%").arg(str(70))) - QtCore.QCoreApplication.processEvents(QtCore.QEventLoop.AllEvents, 1000) + self.updateStatusBar(_translate( + "MainWindow", "Saving settings... %1%").arg(70)) + QtCore.QCoreApplication.processEvents( + QtCore.QEventLoop.AllEvents, 1000 + ) self.saveSettings() for attr, obj in self.ui.__dict__.iteritems(): - if hasattr(obj, "__class__") and isinstance(obj, settingsmixin.SettingsMixin): + if hasattr(obj, "__class__") \ + and isinstance(obj, settingsmixin.SettingsMixin): saveMethod = getattr(obj, "saveSettings", None) - if callable (saveMethod): + if callable(saveMethod): obj.saveSettings() - self.statusBar().showMessage(_translate("MainWindow", "Shutting down core... %1%").arg(str(80))) - QtCore.QCoreApplication.processEvents(QtCore.QEventLoop.AllEvents, 1000) + self.updateStatusBar(_translate( + "MainWindow", "Shutting down core... %1%").arg(80)) + QtCore.QCoreApplication.processEvents( + QtCore.QEventLoop.AllEvents, 1000 + ) shutdown.doCleanShutdown() - self.statusBar().showMessage(_translate("MainWindow", "Stopping notifications... %1%").arg(str(90))) + self.updateStatusBar(_translate( + "MainWindow", "Stopping notifications... %1%").arg(90)) self.tray.hide() - self.statusBar().showMessage(_translate("MainWindow", "Shutdown imminent... %1%").arg(str(100))) + self.updateStatusBar(_translate( + "MainWindow", "Shutdown imminent... %1%").arg(100)) shared.thisapp.cleanup() logger.info("Shutdown complete") super(MyForm, myapp).close() - #return + # return os._exit(0) # window close event @@ -2991,11 +3086,15 @@ class MyForm(settingsmixin.SMainWindow): label, addressAtCurrentInboxRow, True) self.ui.blackwhitelist.rerenderBlackWhiteList() - self.statusBar().showMessage(_translate( - "MainWindow", "Entry added to the blacklist. Edit the label to your liking."), 10000) + self.updateStatusBar(_translate( + "MainWindow", + "Entry added to the blacklist. Edit the label to your liking.") + ) else: - self.statusBar().showMessage(_translate( - "MainWindow", "Error: You cannot add the same address to your blacklist twice. Try renaming the existing one if you want."), 10000) + self.updateStatusBar(_translate( + "MainWindow", + "Error: You cannot add the same address to your blacklist" + " twice. Try renaming the existing one if you want.")) def deleteRowFromMessagelist(self, row = None, inventoryHash = None, ackData = None, messageLists = None): if messageLists is None: @@ -3047,9 +3146,9 @@ class MyForm(settingsmixin.SMainWindow): tableWidget.selectRow(0 if currentRow == 0 else currentRow - 1) tableWidget.setUpdatesEnabled(True) self.propagateUnreadCount(self.getCurrentAccount, folder) - self.statusBar().showMessage(_translate( - "MainWindow", "Moved items to trash."), 10000) - + self.updateStatusBar(_translate( + "MainWindow", "Moved items to trash.")) + def on_action_TrashUndelete(self): tableWidget = self.getCurrentMessagelist() if not tableWidget: @@ -3078,8 +3177,7 @@ class MyForm(settingsmixin.SMainWindow): tableWidget.selectRow(0 if currentRow == 0 else currentRow - 1) tableWidget.setUpdatesEnabled(True) self.propagateUnreadCount(self.getCurrentAccount) - self.statusBar().showMessage(_translate( - "MainWindow", "Undeleted items."), 10000) + self.updateStatusBar(_translate("MainWindow", "Undeleted item.")) def on_action_InboxSaveMessageAs(self): tableWidget = self.getCurrentMessagelist() @@ -3109,9 +3207,9 @@ class MyForm(settingsmixin.SMainWindow): f = open(filename, 'w') f.write(message) f.close() - except Exception, e: + except Exception: logger.exception('Message not saved', exc_info=True) - self.statusBar().showMessage(_translate("MainWindow", "Write error."), 10000) + self.updateStatusBar(_translate("MainWindow", "Write error.")) # Send item on the Sent tab to trash def on_action_SentTrash(self): @@ -3134,12 +3232,11 @@ class MyForm(settingsmixin.SMainWindow): self.propagateUnreadCount(tableWidget.item(currentRow, 1 if tableWidget.item(currentRow, 1).type == AccountMixin.SUBSCRIPTION else 0).data(QtCore.Qt.UserRole), folder, self.getCurrentTreeWidget(), -1) self.getCurrentMessageTextedit().setPlainText("") tableWidget.removeRow(currentRow) - self.statusBar().showMessage(_translate( - "MainWindow", "Moved items to trash."), 10000) - if currentRow == 0: - self.ui.tableWidgetInbox.selectRow(currentRow) - else: - self.ui.tableWidgetInbox.selectRow(currentRow - 1) + self.updateStatusBar(_translate( + "MainWindow", "Moved items to trash.")) + + self.ui.tableWidgetInbox.selectRow( + currentRow if currentRow == 0 else currentRow - 1) def on_action_ForceSend(self): currentRow = self.ui.tableWidgetInbox.currentRow() @@ -3214,10 +3311,10 @@ class MyForm(settingsmixin.SMainWindow): self.ui.lineEditTo.setText(unicode( self.ui.lineEditTo.text().toUtf8(), encoding="UTF-8") + '; ' + stringToAdd) if listOfSelectedRows == {}: - self.statusBar().showMessage(_translate( - "MainWindow", "No addresses selected."), 10000) + self.updateStatusBar(_translate( + "MainWindow", "No addresses selected.")) else: - self.statusBar().clearMessage() + self.statusbar.clearMessage() self.ui.tabWidget.setCurrentIndex( self.ui.tabWidget.indexOf(self.ui.send) ) @@ -3230,11 +3327,11 @@ class MyForm(settingsmixin.SMainWindow): addressAtCurrentRow = str(self.ui.tableWidgetAddressBook.item(currentRow,1).text()) # Then subscribe to it... provided it's not already in the address book if shared.isAddressInMySubscriptionsList(addressAtCurrentRow): - self.statusBar().showMessage(_translate( + self.updateStatusBar(_translate( "MainWindow", "Error: You cannot add the same address to your" " subscriptions twice. Perhaps rename the existing" - " one if you want."), 10000) + " one if you want.")) continue labelAtCurrentRow = self.ui.tableWidgetAddressBook.item(currentRow,0).text().toUtf8() self.addSubscription(addressAtCurrentRow, labelAtCurrentRow) @@ -3991,9 +4088,9 @@ class MyForm(settingsmixin.SMainWindow): logger.info('Status bar: ' + message) if option == 1: - self.statusBar().addImportant(message) + self.statusbar.addImportant(message) else: - self.statusBar().showMessage(message, 10000) + self.statusbar.showMessage(message, 10000) def initSettings(self): QtCore.QCoreApplication.setOrganizationName("PyBitmessage") @@ -4001,9 +4098,10 @@ class MyForm(settingsmixin.SMainWindow): QtCore.QCoreApplication.setApplicationName("pybitmessageqt") self.loadSettings() for attr, obj in self.ui.__dict__.iteritems(): - if hasattr(obj, "__class__") and isinstance(obj, settingsmixin.SettingsMixin): + if hasattr(obj, "__class__") and \ + isinstance(obj, settingsmixin.SettingsMixin): loadMethod = getattr(obj, "loadSettings", None) - if callable (loadMethod): + if callable(loadMethod): obj.loadSettings() diff --git a/src/bitmessageqt/address_dialogs.py b/src/bitmessageqt/address_dialogs.py index 0492dbec..52137890 100644 --- a/src/bitmessageqt/address_dialogs.py +++ b/src/bitmessageqt/address_dialogs.py @@ -8,6 +8,7 @@ from tr import _translate from retranslateui import RetranslateMixin import widgets +import queues import hashlib from inventory import Inventory @@ -32,7 +33,9 @@ class AddressCheckMixin(object): self._onSuccess(addressVersion, streamNumber, ripe) elif status == 'missingbm': self.labelAddressCheck.setText(_translate( - "MainWindow", "The address should start with ''BM-''")) + "MainWindow", # dialog name should be here + "The address should start with ''BM-''" + )) elif status == 'checksumfailed': self.labelAddressCheck.setText(_translate( "MainWindow", @@ -47,7 +50,9 @@ class AddressCheckMixin(object): )) elif status == 'invalidcharacters': self.labelAddressCheck.setText(_translate( - "MainWindow", "The address contains invalid characters.")) + "MainWindow", + "The address contains invalid characters." + )) elif status == 'ripetooshort': self.labelAddressCheck.setText(_translate( "MainWindow", @@ -55,7 +60,9 @@ class AddressCheckMixin(object): )) elif status == 'ripetoolong': self.labelAddressCheck.setText(_translate( - "MainWindow", "Some data encoded in the address is too long.")) + "MainWindow", + "Some data encoded in the address is too long." + )) elif status == 'varintmalformed': self.labelAddressCheck.setText(_translate( "MainWindow", @@ -152,7 +159,7 @@ class SpecialAddressBehaviorDialog(QtGui.QDialog, RetranslateMixin): if self.address_is_chan: # address is a chan address self.radioButtonBehaviorMailingList.setDisabled(True) self.lineEditMailingListName.setText(_translate( - "MainWindow", + "SpecialAddressBehaviorDialog", "This is a chan address. You cannot use it as a" " pseudo-mailing list." )) @@ -240,10 +247,8 @@ class EmailGatewayDialog(QtGui.QDialog, RetranslateMixin): self.config.set(acct.fromAddress, 'label', email) self.config.set(acct.fromAddress, 'gateway', 'mailchuck') self.config.save() - self.parent.statusBar().showMessage(_translate( - "MainWindow", - "Sending email gateway registration request" - ), 10000) + self.parent.statusbar_message( + "Sending email gateway registration request") def accept(self): self.hide() @@ -260,15 +265,15 @@ class EmailGatewayDialog(QtGui.QDialog, RetranslateMixin): self.acct.unregister() self.config.remove_option(self.acct.fromAddress, 'gateway') self.config.save() - self.parent.statusBar().showMessage(_translate( - "MainWindow", + queues.UISignalQueue.put(('updateStatusBar', _translate( + "EmailGatewayDialog", "Sending email gateway unregistration request" - ), 10000) + ))) elif self.radioButtonStatus.isChecked(): self.acct.status() - self.parent.statusBar().showMessage(_translate( - "MainWindow", + queues.UISignalQueue.put(('updateStatusBar', _translate( + "EmailGatewayDialog", "Sending email gateway status request" - ), 10000) + ))) elif self.radioButtonSettings.isChecked(): return self.acct From afcd83a43450f9f92b11bba24ec43769bc30b668 Mon Sep 17 00:00:00 2001 From: Dmitri Bogomolov <4glitch@gmail.com> Date: Wed, 24 Jan 2018 17:27:51 +0200 Subject: [PATCH 350/407] This is probably the right way to handle modal dialogs --- src/bitmessageqt/__init__.py | 207 ++++++++++------------------ src/bitmessageqt/address_dialogs.py | 117 ++++++++++++---- 2 files changed, 168 insertions(+), 156 deletions(-) diff --git a/src/bitmessageqt/__init__.py b/src/bitmessageqt/__init__.py index ba4e3fd0..e0166962 100644 --- a/src/bitmessageqt/__init__.py +++ b/src/bitmessageqt/__init__.py @@ -2225,44 +2225,31 @@ class MyForm(settingsmixin.SMainWindow): if hasattr(acct, "feedback") \ and acct.feedback != GatewayAccount.ALL_OK: if acct.feedback == GatewayAccount.REGISTRATION_DENIED: - dialog = dialogs.EmailGatewayDialog( - self, - _translate( - "EmailGatewayRegistrationDialog", - "Registration failed:"), - _translate( - "EmailGatewayRegistrationDialog", - "The requested email address is not available," - " please try a new one."), - config=BMConfigParser() - ) - if dialog.exec_(): - dialog.register(acct) + dialogs.EmailGatewayDialog( + self, BMConfigParser(), acct).exec_() def click_pushButtonAddAddressBook(self, dialog=None): if not dialog: dialog = dialogs.AddAddressDialog(self) - if dialog.exec_(): - if not dialog.valid: - self.updateStatusBar(_translate( - "MainWindow", - "The address you entered was invalid. Ignoring it." - )) - return - address = addBMIfNotPresent(str(dialog.lineEditAddress.text())) - # First we must check to see if the address is already in the - # address book. The user cannot add it again or else it will - # cause problems when updating and deleting the entry. - if shared.isAddressInMyAddressBook(address): - self.updateStatusBar(_translate( - "MainWindow", - "Error: You cannot add the same address to your" - " address book twice. Try renaming the existing one" - " if you want." - )) - return - label = str(dialog.lineEditLabel.text().toUtf8()) - self.addEntryToAddressBook(address, label) + dialog.exec_() + try: + address, label = dialog.data + except AttributeError: + return + + # First we must check to see if the address is already in the + # address book. The user cannot add it again or else it will + # cause problems when updating and deleting the entry. + if shared.isAddressInMyAddressBook(address): + self.updateStatusBar(_translate( + "MainWindow", + "Error: You cannot add the same address to your" + " address book twice. Try renaming the existing one" + " if you want." + )) + return + + self.addEntryToAddressBook(address, label) def addEntryToAddressBook(self, address, label): if shared.isAddressInMyAddressBook(address): @@ -2289,35 +2276,33 @@ class MyForm(settingsmixin.SMainWindow): def click_pushButtonAddSubscription(self): dialog = dialogs.NewSubscriptionDialog(self) - if dialog.exec_(): - if not dialog.valid: - self.updateStatusBar(_translate( - "MainWindow", - "The address you entered was invalid. Ignoring it." + dialog.exec_() + try: + address, label = dialog.data + except AttributeError: + return + + # We must check to see if the address is already in the + # subscriptions list. The user cannot add it again or else it + # will cause problems when updating and deleting the entry. + if shared.isAddressInMySubscriptionsList(address): + self.updateStatusBar(_translate( + "MainWindow", + "Error: You cannot add the same address to your" + " subscriptions twice. Perhaps rename the existing one" + " if you want." + )) + return + + self.addSubscription(address, label) + # Now, if the user wants to display old broadcasts, let's get + # them out of the inventory and put them + # to the objectProcessorQueue to be processed + if dialog.checkBoxDisplayMessagesAlreadyInInventory.isChecked(): + for value in dialog.recent: + queues.objectProcessorQueue.put(( + value.type, value.payload )) - return - address = addBMIfNotPresent(str(dialog.lineEditAddress.text())) - # We must check to see if the address is already in the - # subscriptions list. The user cannot add it again or else it - # will cause problems when updating and deleting the entry. - if shared.isAddressInMySubscriptionsList(address): - self.updateStatusBar(_translate( - "MainWindow", - "Error: You cannot add the same address to your" - " subscriptions twice. Perhaps rename the existing one" - " if you want." - )) - return - label = str(dialog.lineEditLabel.text().toUtf8()) - self.addSubscription(address, label) - # Now, if the user wants to display old broadcasts, let's get - # them out of the inventory and put them - # to the objectProcessorQueue to be processed - if dialog.checkBoxDisplayMessagesAlreadyInInventory.isChecked(): - for value in dialog.recent: - queues.objectProcessorQueue.put(( - value.type, value.payload - )) def click_pushButtonStatusIcon(self): dialogs.IconGlossaryDialog(self, config=BMConfigParser()).exec_() @@ -2557,29 +2542,32 @@ class MyForm(settingsmixin.SMainWindow): def on_action_EmailGatewayDialog(self): dialog = dialogs.EmailGatewayDialog(self, config=BMConfigParser()) # For Modal dialogs - acct = dialog.exec_() + dialog.exec_() + try: + acct = dialog.data + except AttributeError: + return - # Only settings ramain here - if acct: - acct.settings() - for i in range(self.ui.comboBoxSendFrom.count()): - if str(self.ui.comboBoxSendFrom.itemData(i).toPyObject()) \ - == acct.fromAddress: - self.ui.comboBoxSendFrom.setCurrentIndex(i) - break - else: - self.ui.comboBoxSendFrom.setCurrentIndex(0) + # Only settings remain here + acct.settings() + for i in range(self.ui.comboBoxSendFrom.count()): + if str(self.ui.comboBoxSendFrom.itemData(i).toPyObject()) \ + == acct.fromAddress: + self.ui.comboBoxSendFrom.setCurrentIndex(i) + break + else: + self.ui.comboBoxSendFrom.setCurrentIndex(0) - self.ui.lineEditTo.setText(acct.toAddress) - self.ui.lineEditSubject.setText(acct.subject) - self.ui.textEditMessage.setText(acct.message) - self.ui.tabWidgetSend.setCurrentIndex( - self.ui.tabWidgetSend.indexOf(self.ui.sendDirect) - ) - self.ui.tabWidget.setCurrentIndex( - self.ui.tabWidget.indexOf(self.ui.send) - ) - self.ui.textEditMessage.setFocus() + self.ui.lineEditTo.setText(acct.toAddress) + self.ui.lineEditSubject.setText(acct.subject) + self.ui.textEditMessage.setText(acct.message) + self.ui.tabWidgetSend.setCurrentIndex( + self.ui.tabWidgetSend.indexOf(self.ui.sendDirect) + ) + self.ui.tabWidget.setCurrentIndex( + self.ui.tabWidget.indexOf(self.ui.send) + ) + self.ui.textEditMessage.setFocus() def on_action_MarkAllRead(self): if QtGui.QMessageBox.question( @@ -2621,53 +2609,7 @@ class MyForm(settingsmixin.SMainWindow): addressAtCurrentRow, self.getCurrentFolder(), None, 0) def click_NewAddressDialog(self): - dialog = dialogs.NewAddressDialog(self) - # For Modal dialogs - if not dialog.exec_(): - logger.debug('new address dialog box rejected') - return - - # dialog.buttonBox.enabled = False - if dialog.radioButtonRandomAddress.isChecked(): - if dialog.radioButtonMostAvailable.isChecked(): - streamNumberForAddress = 1 - else: - # User selected 'Use the same stream as an existing - # address.' - streamNumberForAddress = decodeAddress( - dialog.comboBoxExisting.currentText())[2] - queues.addressGeneratorQueue.put(( - 'createRandomAddress', 4, streamNumberForAddress, - str(dialog.newaddresslabel.text().toUtf8()), 1, "", - dialog.checkBoxEighteenByteRipe.isChecked() - )) - else: - if dialog.lineEditPassphrase.text() != \ - dialog.lineEditPassphraseAgain.text(): - QtGui.QMessageBox.about( - self, _translate("MainWindow", "Passphrase mismatch"), - _translate( - "MainWindow", - "The passphrase you entered twice doesn\'t" - " match. Try again.") - ) - elif dialog.lineEditPassphrase.text() == "": - QtGui.QMessageBox.about( - self, _translate("MainWindow", "Choose a passphrase"), - _translate( - "MainWindow", "You really do need a passphrase.") - ) - else: - # this will eventually have to be replaced by logic - # to determine the most available stream number. - streamNumberForAddress = 1 - queues.addressGeneratorQueue.put(( - 'createDeterministicAddresses', 4, streamNumberForAddress, - "unused deterministic address", - dialog.spinBoxNumberOfAddressesToMake.value(), - dialog.lineEditPassphrase.text().toUtf8(), - dialog.checkBoxEighteenByteRipe.isChecked() - )) + dialogs.NewAddressDialog(self) def network_switch(self): dontconnect_option = not BMConfigParser().safeGetBoolean( @@ -3063,9 +3005,8 @@ class MyForm(settingsmixin.SMainWindow): self.ui.tabWidget.setCurrentIndex( self.ui.tabWidget.indexOf(self.ui.send) ) - dialog = dialogs.AddAddressDialog(self) - dialog.lineEditAddress.setText(addressAtCurrentInboxRow) - self.click_pushButtonAddAddressBook(dialog) + self.click_pushButtonAddAddressBook( + dialogs.AddAddressDialog(self, addressAtCurrentInboxRow)) def on_action_InboxAddSenderToBlackList(self): tableWidget = self.getCurrentMessagelist() diff --git a/src/bitmessageqt/address_dialogs.py b/src/bitmessageqt/address_dialogs.py index 52137890..dde08409 100644 --- a/src/bitmessageqt/address_dialogs.py +++ b/src/bitmessageqt/address_dialogs.py @@ -1,5 +1,5 @@ from PyQt4 import QtCore, QtGui -from addresses import decodeAddress, encodeVarint +from addresses import decodeAddress, encodeVarint, addBMIfNotPresent from account import ( GatewayAccount, MailchuckAccount, AccountMixin, accountClass, getSortedAccounts @@ -70,12 +70,33 @@ class AddressCheckMixin(object): )) -class AddAddressDialog(QtGui.QDialog, RetranslateMixin, AddressCheckMixin): +class AddressDataDialog(QtGui.QDialog, AddressCheckMixin): + def __init__(self, parent): + super(AddressDataDialog, self).__init__(parent) + self.parent = parent - def __init__(self, parent=None): + def accept(self): + if self.valid: + self.data = ( + addBMIfNotPresent(str(self.lineEditAddress.text())), + str(self.lineEditLabel.text().toUtf8()) + ) + else: + queues.UISignalQueue.put(('updateStatusBar', _translate( + "MainWindow", + "The address you entered was invalid. Ignoring it." + ))) + super(AddressDataDialog, self).accept() + + +class AddAddressDialog(AddressDataDialog, RetranslateMixin): + + def __init__(self, parent=None, address=None): super(AddAddressDialog, self).__init__(parent) widgets.load('addaddressdialog.ui', self) AddressCheckMixin.__init__(self) + if address: + self.lineEditAddress.setText(address) class NewAddressDialog(QtGui.QDialog, RetranslateMixin): @@ -91,10 +112,54 @@ class NewAddressDialog(QtGui.QDialog, RetranslateMixin): self.comboBoxExisting.addItem(address) self.groupBoxDeterministic.setHidden(True) QtGui.QWidget.resize(self, QtGui.QWidget.sizeHint(self)) + self.show() + + def accept(self): + self.hide() + # self.buttonBox.enabled = False + if self.radioButtonRandomAddress.isChecked(): + if self.radioButtonMostAvailable.isChecked(): + streamNumberForAddress = 1 + else: + # User selected 'Use the same stream as an existing + # address.' + streamNumberForAddress = decodeAddress( + self.comboBoxExisting.currentText())[2] + queues.addressGeneratorQueue.put(( + 'createRandomAddress', 4, streamNumberForAddress, + str(self.newaddresslabel.text().toUtf8()), 1, "", + self.checkBoxEighteenByteRipe.isChecked() + )) + else: + if self.lineEditPassphrase.text() != \ + self.lineEditPassphraseAgain.text(): + QtGui.QMessageBox.about( + self, _translate("MainWindow", "Passphrase mismatch"), + _translate( + "MainWindow", + "The passphrase you entered twice doesn\'t" + " match. Try again.") + ) + elif self.lineEditPassphrase.text() == "": + QtGui.QMessageBox.about( + self, _translate("MainWindow", "Choose a passphrase"), + _translate( + "MainWindow", "You really do need a passphrase.") + ) + else: + # this will eventually have to be replaced by logic + # to determine the most available stream number. + streamNumberForAddress = 1 + queues.addressGeneratorQueue.put(( + 'createDeterministicAddresses', 4, streamNumberForAddress, + "unused deterministic address", + self.spinBoxNumberOfAddressesToMake.value(), + self.lineEditPassphrase.text().toUtf8(), + self.checkBoxEighteenByteRipe.isChecked() + )) -class NewSubscriptionDialog( - QtGui.QDialog, RetranslateMixin, AddressCheckMixin): +class NewSubscriptionDialog(AddressDataDialog, RetranslateMixin): def __init__(self, parent=None): super(NewSubscriptionDialog, self).__init__(parent) @@ -206,14 +271,20 @@ class SpecialAddressBehaviorDialog(QtGui.QDialog, RetranslateMixin): class EmailGatewayDialog(QtGui.QDialog, RetranslateMixin): - def __init__(self, parent, title=None, label=None, config=None): + def __init__(self, parent, config=None, account=None): super(EmailGatewayDialog, self).__init__(parent) widgets.load('emailgateway.ui', self) self.parent = parent self.config = config - if title and label: - self.setWindowTitle(title) - self.label.setText(label) + if account: + self.acct = account + self.setWindowTitle(_translate( + "EmailGatewayDialog", "Registration failed:")) + self.label.setText(_translate( + "EmailGatewayDialog", + "The requested email address is not available," + " please try a new one." + )) self.radioButtonRegister.hide() self.radioButtonStatus.hide() self.radioButtonSettings.hide() @@ -239,17 +310,6 @@ class EmailGatewayDialog(QtGui.QDialog, RetranslateMixin): self.lineEditEmail.setFocus() QtGui.QWidget.resize(self, QtGui.QWidget.sizeHint(self)) - def register(self, acct=None): - email = str(self.lineEditEmail.text().toUtf8()) - if acct is None: - acct = self.acct - acct.register(email) - self.config.set(acct.fromAddress, 'label', email) - self.config.set(acct.fromAddress, 'gateway', 'mailchuck') - self.config.save() - self.parent.statusbar_message( - "Sending email gateway registration request") - def accept(self): self.hide() # no chans / mailinglists @@ -259,8 +319,17 @@ class EmailGatewayDialog(QtGui.QDialog, RetranslateMixin): if not isinstance(self.acct, GatewayAccount): return - if self.radioButtonRegister.isChecked(): - self.register() + if self.radioButtonRegister.isChecked() \ + or self.radioButtonRegister.isHidden(): + email = str(self.lineEditEmail.text().toUtf8()) + self.acct.register(email) + self.config.set(self.acct.fromAddress, 'label', email) + self.config.set(self.acct.fromAddress, 'gateway', 'mailchuck') + self.config.save() + queues.UISignalQueue.put(('updateStatusBar', _translate( + "EmailGatewayDialog", + "Sending email gateway registration request" + ))) elif self.radioButtonUnregister.isChecked(): self.acct.unregister() self.config.remove_option(self.acct.fromAddress, 'gateway') @@ -276,4 +345,6 @@ class EmailGatewayDialog(QtGui.QDialog, RetranslateMixin): "Sending email gateway status request" ))) elif self.radioButtonSettings.isChecked(): - return self.acct + self.data = self.acct + + super(EmailGatewayDialog, self).accept() From c92c2aebc2271736f9567efb7492985ccd07fb09 Mon Sep 17 00:00:00 2001 From: Dmitri Bogomolov <4glitch@gmail.com> Date: Sat, 27 Jan 2018 16:40:01 +0200 Subject: [PATCH 351/407] Blank title of RegenerateAddressesDialog also --- src/bitmessageqt/address_dialogs.py | 1 + 1 file changed, 1 insertion(+) diff --git a/src/bitmessageqt/address_dialogs.py b/src/bitmessageqt/address_dialogs.py index dde08409..7b619625 100644 --- a/src/bitmessageqt/address_dialogs.py +++ b/src/bitmessageqt/address_dialogs.py @@ -202,6 +202,7 @@ class RegenerateAddressesDialog(QtGui.QDialog, RetranslateMixin): def __init__(self, parent=None): super(RegenerateAddressesDialog, self).__init__(parent) widgets.load('regenerateaddresses.ui', self) + self.groupBox.setTitle('') QtGui.QWidget.resize(self, QtGui.QWidget.sizeHint(self)) From 80ca82ad8b6da8e20c4d14711289309a8161ba99 Mon Sep 17 00:00:00 2001 From: PyBitmessage Translations Date: Tue, 30 Jan 2018 16:33:27 +0100 Subject: [PATCH 352/407] Auto-updated language ru from transifex --- src/translations/bitmessage_ru.qm | Bin 94127 -> 94216 bytes src/translations/bitmessage_ru.ts | 456 ++++++++++++++++-------------- 2 files changed, 238 insertions(+), 218 deletions(-) diff --git a/src/translations/bitmessage_ru.qm b/src/translations/bitmessage_ru.qm index 82604f65804e3eb917008e1b63b0678f848e6585..bd61a1fa8c6b498c84f83d81d39131e62154eabf 100644 GIT binary patch delta 3390 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 E0aV)Sod5s; delta 3290 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 diff --git a/src/translations/bitmessage_ru.ts b/src/translations/bitmessage_ru.ts index 0473d209..3e020294 100644 --- a/src/translations/bitmessage_ru.ts +++ b/src/translations/bitmessage_ru.ts @@ -58,12 +58,12 @@ EmailGatewayRegistrationDialog - + Registration failed: Регистрация не удалась: - + The requested email address is not available, please try a new one. Fill out the new desired email address (including @mailchuck.com) below: Запрашиваемый адрес email недоступен, попробуйте ввести другой. Введите желаемый адрес (включая @mailchuck.com) ниже: @@ -83,7 +83,7 @@ Please type the desired email address (including @mailchuck.com) below: Mailchuck - + # You can use this to configure your email gateway account # Uncomment the setting you want to use # Here are the options: @@ -168,52 +168,52 @@ Please type the desired email address (including @mailchuck.com) below: MainWindow - + Reply to sender Ответить отправителю - + Reply to channel Ответить в канал - + Add sender to your Address Book Добавить отправителя в адресную книгу - + Add sender to your Blacklist Добавить отправителя в чёрный список - + Move to Trash Поместить в корзину - + Undelete Отменить удаление - + View HTML code as formatted text Просмотреть HTML код как отформатированный текст - + Save message as... Сохранить сообщение как ... - + Mark Unread Отметить как непрочитанное - + New Новый адрес @@ -238,12 +238,12 @@ Please type the desired email address (including @mailchuck.com) below: Скопировать адрес в буфер обмена - + Special address behavior... Особое поведение адресов... - + Email gateway Email-шлюз @@ -253,37 +253,37 @@ Please type the desired email address (including @mailchuck.com) below: Удалить
- + Send message to this address Отправить сообщение на этот адрес - + Subscribe to this address Подписаться на рассылку с этого адреса - + Add New Address Добавить новый адрес - + Copy destination address to clipboard Скопировать адрес отправки в буфер обмена - + Force send Форсировать отправку - + One of your addresses, %1, is an old version 1 address. Version 1 addresses are no longer supported. May we delete it now? Один из Ваших адресов, %1, является устаревшим адресом версии 1. Адреса версии 1 больше не поддерживаются. Хотите ли Вы удалить его сейчас? - + Waiting for their encryption key. Will request it again soon. Ожидаем ключ шифрования от Вашего собеседника. Запрос будет повторен через некоторое время. @@ -293,17 +293,17 @@ Please type the desired email address (including @mailchuck.com) below:
- + Queued. В очереди. - + Message sent. Waiting for acknowledgement. Sent at %1 Сообщение отправлено. Ожидаем подтверждения. Отправлено в %1 - + Message sent. Sent at %1 Сообщение отправлено в %1 @@ -313,78 +313,78 @@ Please type the desired email address (including @mailchuck.com) below:
- + Acknowledgement of the message received %1 Доставлено в %1 - + Broadcast queued. Рассылка ожидает очереди. - + Broadcast on %1 Рассылка на %1 - + Problem: The work demanded by the recipient is more difficult than you are willing to do. %1 Проблема: Ваш получатель требует более сложных вычислений, чем максимум, указанный в Ваших настройках. %1 - + Problem: The recipient's encryption key is no good. Could not encrypt message. %1 Проблема: ключ получателя неправильный. Невозможно зашифровать сообщение. %1 - + Forced difficulty override. Send should start soon. Форсирована смена сложности. Отправляем через некоторое время. - + Unknown status: %1 %2 Неизвестный статус: %1 %2 - + Not Connected Не соединено - + Show Bitmessage Показать Bitmessage - + Send Отправить - + Subscribe Подписки - + Channel Канал - + Quit Выйти - + You may manage your keys by editing the keys.dat file stored in the same directory as this program. It is important that you back up this file. Вы можете управлять Вашими ключами, редактируя файл keys.dat, находящийся в той же папке, что и эта программа. Создайте резервную копию этого файла перед тем как будете его редактировать. - + You may manage your keys by editing the keys.dat file stored in %1 It is important that you back up this file. @@ -393,19 +393,19 @@ It is important that you back up this file. Создайте резервную копию этого файла перед тем как будете его редактировать. - + Open keys.dat? Открыть файл keys.dat? - + You may manage your keys by editing the keys.dat file stored in the same directory as this program. It is important that you back up this file. Would you like to open the file now? (Be sure to close Bitmessage before making any changes.) Вы можете управлять Вашими ключами, редактируя файл keys.dat, находящийся в той же папке, что и эта программа. Создайте резервную копию этого файла перед тем как будете его редактировать. Хотели бы Вы открыть этот файл сейчас? (пожалуйста, закройте Bitmessage до того как Вы внесёте в этот файл какие-либо изменения.) - + You may manage your keys by editing the keys.dat file stored in %1 It is important that you back up this file. Would you like to open the file now? (Be sure to close Bitmessage before making any changes.) @@ -415,37 +415,37 @@ It is important that you back up this file. Would you like to open the file now? (пожалуйста, закройте Bitmessage до того как Вы внесёте в этот файл какие-либо изменения.) - + Delete trash? Очистить корзину? - + Are you sure you want to delete all trashed messages? Вы уверены что хотите очистить корзину? - + bad passphrase Неподходящая секретная фраза - + You must type your passphrase. If you don't have one then this is not the form for you. Вы должны ввести секретную фразу. Если Вы не хотите этого делать, то Вы выбрали неправильную опцию. - + Bad address version number Неверный номер версии адреса - + Your address version number must be a number: either 3 or 4. Адрес номера версии должен быть числом: либо 3, либо 4. - + Your address version number must be either 3 or 4. Адрес номера версии должен быть либо 3, либо 4. @@ -515,22 +515,22 @@ It is important that you back up this file. Would you like to open the file now?
- + Connection lost Соединение потеряно - + Connected Соединено - + Message trashed Сообщение удалено - + The TTL, or Time-To-Live is the length of time that the network will hold the message. The recipient must get it during this time. If your Bitmessage client does not hear an acknowledgement, it will resend the message automatically. The longer the Time-To-Live, the @@ -541,17 +541,17 @@ It is important that you back up this file. Would you like to open the file now? сообщение. Часто разумным вариантом будет установка TTL на 4 или 5 дней. - + Message too long Сообщение слишком длинное - + The message that you are trying to send is too long by %1 bytes. (The maximum is 261644 bytes). Please cut it down before sending. Сообщение, которое вы пытаетесь отправить, длиннее максимально допустимого на %1 байт. (Максимально допустимое значение 261644 байта). Пожалуйста, сократите сообщение перед отправкой. - + Error: Your account wasn't registered at an email gateway. Sending registration now as %1, please wait for the registration to be processed before retrying sending. Ошибка: ваш аккаунт не зарегистрирован на Email-шлюзе. Отправка регистрации %1, пожалуйста, подождите пока процесс регистрации не завершится, прежде чем попытаться отправить сообщение заново. @@ -596,57 +596,57 @@ It is important that you back up this file. Would you like to open the file now?
- + Error: You must specify a From address. If you don't have one, go to the 'Your Identities' tab. Вы должны указать адрес в поле "От кого". Вы можете найти Ваш адрес во вкладке "Ваши Адреса". - + Address version number Версия адреса - + Concerning the address %1, Bitmessage cannot understand address version numbers of %2. Perhaps upgrade Bitmessage to the latest version. По поводу адреса %1: Bitmessage не поддерживаем адреса версии %2. Возможно, Вам нужно обновить клиент Bitmessage. - + Stream number Номер потока - + Concerning the address %1, Bitmessage cannot handle stream numbers of %2. Perhaps upgrade Bitmessage to the latest version. По поводу адреса %1: Bitmessage не поддерживаем стрим номер %2. Возможно, Вам нужно обновить клиент Bitmessage. - + Warning: You are currently not connected. Bitmessage will do the work necessary to send the message but it won't send until you connect. Внимание: Вы не подключены к сети. Bitmessage выполнит работу, требуемую для отправки сообщения, но не отправит его до тех пор, пока Вы не подключитесь. - + Message queued. Сообщение в очереди. - + Your 'To' field is empty. Вы не заполнили поле 'Кому'. - + Right click one or more entries in your address book and select 'Send message to this address'. Нажмите правую кнопку мыши на каком-либо адресе и выберите "Отправить сообщение на этот адрес". - + Fetched address from namecoin identity. Получить адрес через Namecoin. - + New Message Новое сообщение @@ -656,7 +656,7 @@ It is important that you back up this file. Would you like to open the file now?
- + Sending email gateway registration request Отправка запроса на регистрацию на Email-шлюзе @@ -671,142 +671,142 @@ It is important that you back up this file. Would you like to open the file now? Вы ввели неправильный адрес. Это адрес проигнорирован.
- + Error: You cannot add the same address to your address book twice. Try renaming the existing one if you want. Ошибка: Вы не можете добавлять один и тот же адрес в Адресную Книгу несколько раз. Попробуйте переименовать существующий адрес. - + Error: You cannot add the same address to your subscriptions twice. Perhaps rename the existing one if you want. Ошибка: вы не можете добавить один и тот же адрес в ваши подписки дважды. Пожалуйста, переименуйте имеющийся адрес, если хотите. - + Restart Перезапустить - + You must restart Bitmessage for the port number change to take effect. Вы должны перезапустить Bitmessage, чтобы смена номера порта имела эффект. - + Bitmessage will use your proxy from now on but you may want to manually restart Bitmessage now to close existing connections (if any). Bitmessage будет использовать Ваш прокси, начиная прямо сейчас. Тем не менее Вам имеет смысл перезапустить Bitmessage, чтобы закрыть уже существующие соединения. - + Number needed Требуется число - + Your maximum download and upload rate must be numbers. Ignoring what you typed. Скорости загрузки и выгрузки должны быть числами. Игнорирую то, что вы набрали. - + Will not resend ever Не пересылать никогда - + Note that the time limit you entered is less than the amount of time Bitmessage waits for the first resend attempt therefore your messages will never be resent. Обратите внимание, что лимит времени, который вы ввели, меньше чем время, которое Bitmessage ждет перед первой попыткой переотправки сообщения, поэтому ваши сообщения никогда не будут переотправлены. - + Sending email gateway unregistration request Отправка запроса на отмену регистрации на Email-шлюзе - + Sending email gateway status request Отправка запроса статуса аккаунта на Email-шлюзе - + Passphrase mismatch Секретная фраза не подходит - + The passphrase you entered twice doesn't match. Try again. Вы ввели две разные секретные фразы. Пожалуйста, повторите заново. - + Choose a passphrase Придумайте секретную фразу - + You really do need a passphrase. Вы действительно должны ввести секретную фразу. - + Address is gone Адрес утерян - + Bitmessage cannot find your address %1. Perhaps you removed it? Bitmessage не может найти Ваш адрес %1. Возможно Вы удалили его? - + Address disabled Адрес выключен - + Error: The address from which you are trying to send is disabled. You'll have to enable it on the 'Your Identities' tab before using it. Ошибка: адрес, с которого Вы пытаетесь отправить, выключен. Вам нужно будет включить этот адрес во вкладке "Ваши адреса". - + Entry added to the Address Book. Edit the label to your liking. Запись добавлена в Адресную Книгу. Вы можете её отредактировать. - + Entry added to the blacklist. Edit the label to your liking. Запись добавлена в чёрный список. Измените название по своему вкусу. - + Error: You cannot add the same address to your blacklist twice. Try renaming the existing one if you want. Ошибка: вы не можете добавить один и тот же адрес в чёрный список дважды. Попробуйте переименовать существующий адрес. - + Moved items to trash. Удалено в корзину. - + Undeleted item. - Отменить удаление записи + - + Save As... Сохранить как ... - + Write error. Ошибка записи. - + No addresses selected. Вы не выбрали адрес. - + If you delete the subscription, messages that you already received will become inaccessible. Maybe you can consider disabling the subscription instead. Disabled subscriptions will not receive new messages, but you can still view messages you already received. Are you sure you want to delete the subscription? @@ -815,7 +815,7 @@ Are you sure you want to delete the subscription? Вы уверены, что хотите отменить подписку? - + If you delete the channel, messages that you already received will become inaccessible. Maybe you can consider disabling the channel instead. Disabled channels will not receive new messages, but you can still view messages you already received. Are you sure you want to delete the channel? @@ -824,277 +824,277 @@ Are you sure you want to delete the channel? Вы уверены, что хотите удалить канал? - + Do you really want to remove this avatar? Вы уверены, что хотите удалить этот аватар? - + You have already set an avatar for this address. Do you really want to overwrite it? У вас уже есть аватар для этого адреса. Вы уверены, что хотите перезаписать аватар? - + Start-on-login not yet supported on your OS. Запуск программы при входе в систему ещё не поддерживается в вашей операционной системе. - + Minimize-to-tray not yet supported on your OS. Сворачивание в трей ещё не поддерживается в вашей операционной системе. - + Tray notifications not yet supported on your OS. Уведомления в трее ещё не поддерживаеются в вашей операционной системе. - + Testing... Проверяем... - + This is a chan address. You cannot use it as a pseudo-mailing list. Это адрес chan-а. Вы не можете его использовать как адрес рассылки. - + The address should start with ''BM-'' Адрес должен начинаться с "BM-" - + The address is not typed or copied correctly (the checksum failed). Адрес введен или скопирован неверно (контрольная сумма не сходится). - + The version number of this address is higher than this software can support. Please upgrade Bitmessage. Версия этого адреса более поздняя, чем Ваша программа. Пожалуйста, обновите программу Bitmessage. - + The address contains invalid characters. Адрес содержит запрещённые символы. - + Some data encoded in the address is too short. Данные, закодированные в адресе, слишком короткие. - + Some data encoded in the address is too long. Данные, закодированные в адресе, слишком длинные. - + Some data encoded in the address is malformed. Данные, закодированные в адресе, имеют неверный формат. - + Enter an address above. Введите адрес выше. - + Address is an old type. We cannot display its past broadcasts. Адрес старого типа. Мы не можем отобразить его прошлые рассылки. - + There are no recent broadcasts from this address to display. Нет недавних рассылок с этого адреса для отображения. - + You are using TCP port %1. (This can be changed in the settings). Вы используете TCP порт %1. (Его можно поменять в настройках). - + Bitmessage Bitmessage - + Identities Адреса - + New Identity Создать новый адрес - + Search Поиск - + All По всем полям - + To Кому - + From От кого - + Subject Тема - + Message Текст сообщения - + Received Получено - + Messages Сообщения - + Address book Адресная книга - + Address Адрес - + Add Contact Добавить контакт - + Fetch Namecoin ID Получить Namecoin ID - + Subject: Тема: - + From: От: - + To: Кому: - + Send ordinary Message Отправить обычное сообщение - + Send Message to your Subscribers Отправить сообщение для ваших подписчиков - + TTL: TTL: - + Subscriptions Подписки - + Add new Subscription Добавить новую подписку - + Chans Чаны - + Add Chan Добавить чан - + File Файл - + Settings Настройки - + Help Помощь - + Import keys Импортировать ключи - + Manage keys Управлять ключами - + Ctrl+Q Ctrl+Q - + F1 F1 - + Contact support Связаться с поддержкой - + About О программе - + Regenerate deterministic addresses Сгенерировать заново все адреса - + Delete all trashed messages Стереть все сообщения из корзины - + Join / Create chan Подключить или создать чан @@ -1119,67 +1119,67 @@ Are you sure you want to delete the channel? Добавить новую запись - + Display the %1 recent broadcast(s) from this address. Показать %1 прошлых рассылок с этого адреса. - + New version of PyBitmessage is available: %1. Download it from https://github.com/Bitmessage/PyBitmessage/releases/latest Доступна новая версия PyBitmessage: %1. Загрузите её: https://github.com/Bitmessage/PyBitmessage/releases/latest - + Waiting for PoW to finish... %1% Ожидание окончания PoW... %1% - + Shutting down Pybitmessage... %1% Завершение PyBitmessage... %1% - + Waiting for objects to be sent... %1% Ожидание отправки объектов... %1% - + Saving settings... %1% Сохранение настроек... %1% - + Shutting down core... %1% Завершение работы ядра... %1% - + Stopping notifications... %1% Остановка сервиса уведомлений... %1% - + Shutdown imminent... %1% Завершение вот-вот произойдет... %1% - + %n hour(s) %n час%n часа%n часов%n час(а/ов) - + %n day(s) %n день%n дня%n дней%n дней - + Shutting down PyBitmessage... %1% Завершение PyBitmessage... %1% - + Sent Отправлено @@ -1224,86 +1224,86 @@ Are you sure you want to delete the channel? Внимание: свободное место на диске закончилось. Bitmessage завершит свою работу. - + Error! Could not find sender address (your address) in the keys.dat file. Ошибка: невозможно найти адрес отправителя (ваш адрес) в файле ключей keys.dat - + Doing work necessary to send broadcast... Выполнение работы, требуемой для рассылки... - + Broadcast sent on %1 Рассылка отправлена на %1 - + Encryption key was requested earlier. Ключ шифрования запрошен ранее. - + Sending a request for the recipient's encryption key. Отправка запроса ключа шифрования получателя. - + Looking up the receiver's public key Поиск открытого ключа получателя - + Problem: Destination is a mobile device who requests that the destination be included in the message but this is disallowed in your settings. %1 Проблема: адресат является мобильным устройством, которое требует, чтобы адрес назначения был включен в сообщение, однако, это запрещено в ваших настройках. %1 - + Doing work necessary to send message. There is no required difficulty for version 2 addresses like this. Выполнение работы, требуемой для отправки сообщения. Для адреса версии 2 (как этот), не требуется указание сложности. - + Doing work necessary to send message. Receiver's required difficulty: %1 and %2 Выполнение работы, требуемой для отправки сообщения. Получатель запросил сложность: %1 и %2 - + Problem: The work demanded by the recipient (%1 and %2) is more difficult than you are willing to do. %3 Проблема: сложность, затребованная получателем (%1 и %2) гораздо больше, чем вы готовы сделать. %3 - + Problem: You are trying to send a message to yourself or a chan but your encryption key could not be found in the keys.dat file. Could not encrypt message. %1 Проблема: вы пытаетесь отправить сообщение самому себе или в чан, но ваш ключ шифрования не найден в файле ключей keys.dat. Невозможно зашифровать сообщение. %1 - + Doing work necessary to send message. Выполнение работы, требуемой для отправки сообщения. - + Message sent. Waiting for acknowledgement. Sent on %1 Отправлено. Ожидаем подтверждения. Отправлено в %1 - + Doing work necessary to request encryption key. Выполнение работы, требуемой для запроса ключа шифрования. - + Broadcasting the public key request. This program will auto-retry if they are offline. Рассылка запросов открытого ключа шифрования. Программа будет повторять попытки, если они оффлайн. - + Sending public key request. Waiting for reply. Requested at %1 Отправка запроса открытого ключа шифрования. Ожидание ответа. Запрошено в %1 @@ -1318,37 +1318,37 @@ Receiver's required difficulty: %1 and %2 Распределение портов UPnP отменено - + Mark all messages as read Отметить все сообщения как прочтенные - + Are you sure you would like to mark all messages read? Вы уверены, что хотите отметить все сообщения как прочтенные? - + Doing work necessary to send broadcast. Выполнение работы, требуемой для отправки рассылки. - + Proof of work pending Ожидается доказательство работы - + %n object(s) pending proof of work %n объект в ожидании доказательства работы%n объекта в ожидании доказательства работы%n объектов в ожидании доказательства работы%n объектов в ожидании доказательства работы - + %n object(s) waiting to be distributed %n объект ожидает раздачи%n объекта ожидают раздачи%n объектов ожидают раздачи%n объектов ожидают раздачи - + Wait until these tasks finish? Подождать завершения этих задач? @@ -1413,12 +1413,12 @@ Receiver's required difficulty: %1 and %2 Ваша видеокарта вычислила неправильно, отключаем OpenCL. Пожалуйста, сообщите разработчикам. - + Set notification sound... Установить звук уведомления... - + Welcome to easy and secure Bitmessage * send messages to other people @@ -1432,110 +1432,125 @@ Receiver's required difficulty: %1 and %2 * участвуйте в обсуждениях в чанах - + not recommended for chans не рекомендовано для чанов - + Quiet Mode Тихий режим - + Problems connecting? Try enabling UPnP in the Network Settings Проблемы подключения? Попробуйте включить UPnP в сетевых настройках. - + You are trying to send an email instead of a bitmessage. This requires registering with a gateway. Attempt to register? Вы пытаетесь отправить email вместо bitmessage. Для этого нужно зарегистрироваться на шлюзе. Попробовать зарегистрироваться? - + Error: Bitmessage addresses start with BM- Please check the recipient address %1 Ошибка: адреса Bitmessage начинаются с "BM-". Пожалуйста, проверьте адрес получателя %1. - + Error: The recipient address %1 is not typed or copied correctly. Please check it. Ошибка: адрес получателя %1 набран или скопирован неправильно. Пожалуйста, проверьте его. - + Error: The recipient address %1 contains invalid characters. Please check it. Ошибка: адрес получателя %1 содержит недопустимые символы. Пожалуйста, проверьте его. - + Error: The version of the recipient address %1 is too high. Either you need to upgrade your Bitmessage software or your acquaintance is being clever. Ошибка: версия адреса получателя %1 слишком высокая. Либо вам нужно обновить программу Bitmessage, либо ваш знакомый - умник. - + Error: Some data encoded in the recipient address %1 is too short. There might be something wrong with the software of your acquaintance. Ошибка: часть данных, закодированных в адресе получателя %1 слишком короткая. Видимо, что-то не так с программой, используемой вашим знакомым. - + Error: Some data encoded in the recipient address %1 is too long. There might be something wrong with the software of your acquaintance. Ошибка: часть данных, закодированных в адресе получателя %1 слишком длинная. Видимо, что-то не так с программой, используемой вашим знакомым. - + Error: Some data encoded in the recipient address %1 is malformed. There might be something wrong with the software of your acquaintance. Ошибка: часть данных, закодированных в адресе получателя %1 сформирована неправильно. Видимо, что-то не так с программой, используемой вашим знакомым. - + Error: Something is wrong with the recipient address %1. Ошибка: что-то не так с адресом получателя %1. - + Error: %1 Ошибка: %1 - + From %1 От %1 - + Synchronisation pending Ожидается синхронизация - + Bitmessage hasn't synchronised with the network, %n object(s) to be downloaded. If you quit now, it may cause delivery delays. Wait until the synchronisation finishes? Bitmessage не синхронизирован с сетью, незагруженных объектов: %n. Выход сейчас может привести к задержкам доставки. Подождать завершения синхронизации?Bitmessage не синхронизирован с сетью, незагруженных объектов: %n. Выход сейчас может привести к задержкам доставки. Подождать завершения синхронизации?Bitmessage не синхронизирован с сетью, незагруженных объектов: %n. Выход сейчас может привести к задержкам доставки. Подождать завершения синхронизации?Bitmessage не синхронизирован с сетью, незагруженных объектов: %n. Выход сейчас может привести к задержкам доставки. Подождать завершения синхронизации? - + Not connected Не подключено - + Bitmessage isn't connected to the network. If you quit now, it may cause delivery delays. Wait until connected and the synchronisation finishes? Bitmessage не подключен к сети. Выход сейчас может привести к задержкам доставки. Подождать подключения и завершения синхронизации? - + Waiting for network connection... Ожидание сетевого подключения... - + Waiting for finishing synchronisation... Ожидание окончания синхронизации... - + + Undeleted items. + Восстановленные элементы. + + + You have already set a notification sound for this address book entry. Do you really want to overwrite it? У вас уже есть звук уведомления для этого адресата. Вы уверены, что хотите перезаписать звук уведомления? + + + Go online + Подключиться к сети + + + + Go offline + + MessageView @@ -1804,25 +1819,30 @@ The 'Random Number' option is selected by default but deterministic ad connectDialog - + Bitmessage Bitmessage - + Bitmessage won't connect to anyone until you let it. Bitmessage не будет соединяться ни с кем, пока Вы это не разрешите. - + Connect now Соединиться прямо сейчас - + Let me configure special network settings first Я хочу сперва настроить сетевые настройки + + + Work offline + + helpDialog @@ -1953,7 +1973,7 @@ The 'Random Number' option is selected by default but deterministic ad Загрузка: 0 кБ/с - + Network Status Состояние сети From 9807d3e4b41cbbe38843530a2e7bd859b8312e15 Mon Sep 17 00:00:00 2001 From: Peter Surda Date: Tue, 30 Jan 2018 17:42:10 +0100 Subject: [PATCH 353/407] Don't complain about being disconnected if in offline mode --- src/bitmessageqt/__init__.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/bitmessageqt/__init__.py b/src/bitmessageqt/__init__.py index e0166962..8e836fe1 100644 --- a/src/bitmessageqt/__init__.py +++ b/src/bitmessageqt/__init__.py @@ -2663,7 +2663,8 @@ class MyForm(settingsmixin.SMainWindow): else: PendingDownloadQueue.stop() - if shared.statusIconColor == 'red': + if shared.statusIconColor == 'red' and not BMConfigParser().safeGetBoolean( + 'bitmessagesettings', 'dontconnect'): reply = QtGui.QMessageBox.question(self, _translate("MainWindow", "Not connected"), _translate("MainWindow", "Bitmessage isn't connected to the network. If you quit now, it may cause delivery delays. Wait until connected and the synchronisation finishes?"), QtGui.QMessageBox.Yes|QtGui.QMessageBox.No|QtGui.QMessageBox.Cancel, QtGui.QMessageBox.Cancel) From 175f8b14a508d83ebcae0edb75eb2dd84378923c Mon Sep 17 00:00:00 2001 From: Dmitri Bogomolov <4glitch@gmail.com> Date: Wed, 31 Jan 2018 12:31:23 +0200 Subject: [PATCH 354/407] Update SOURCES and FORMS in bitmessage.pro --- src/translations/bitmessage.pro | 24 +++++++++++++----------- 1 file changed, 13 insertions(+), 11 deletions(-) diff --git a/src/translations/bitmessage.pro b/src/translations/bitmessage.pro index 818d799d..e3c85535 100644 --- a/src/translations/bitmessage.pro +++ b/src/translations/bitmessage.pro @@ -21,33 +21,35 @@ SOURCES = ../addresses.py\ ../shared.py\ ../upnp.py\ ../bitmessageqt/__init__.py\ - ../bitmessageqt/about.py\ ../bitmessageqt/account.py\ - ../bitmessageqt/addaddressdialog.py\ + ../bitmessageqt/address_dialogs.py\ ../bitmessageqt/bitmessageui.py\ ../bitmessageqt/blacklist.py\ - ../bitmessageqt/connect.py\ - ../bitmessageqt/emailgateway.py\ + ../bitmessageqt/dialogs.py\ ../bitmessageqt/foldertree.py\ - ../bitmessageqt/help.py\ - ../bitmessageqt/iconglossary.py\ ../bitmessageqt/languagebox.py\ ../bitmessageqt/messagecompose.py\ ../bitmessageqt/messageview.py\ ../bitmessageqt/networkstatus.py\ - ../bitmessageqt/newaddressdialog.py\ ../bitmessageqt/newchandialog.py\ - ../bitmessageqt/newsubscriptiondialog.py\ - ../bitmessageqt/regenerateaddresses.py\ ../bitmessageqt/safehtmlparser.py\ ../bitmessageqt/settings.py\ - ../bitmessageqt/specialaddressbehavior.py\ ../plugins/qrcodeui.py FORMS = \ + ../bitmessageqt/about.ui\ + ../bitmessageqt/addaddressdialog.ui\ ../bitmessageqt/blacklist.ui\ + ../bitmessageqt/connect.ui\ + ../bitmessageqt/emailgateway.ui\ + ../bitmessageqt/help.ui\ + ../bitmessageqt/iconglossary.ui\ ../bitmessageqt/networkstatus.ui\ - ../bitmessageqt/newchandialog.ui + ../bitmessageqt/newaddressdialog.ui\ + ../bitmessageqt/newchandialog.ui\ + ../bitmessageqt/newsubscriptiondialog.ui\ + ../bitmessageqt/regenerateaddresses.ui\ + ../bitmessageqt/specialaddressbehavior.ui TRANSLATIONS = \ bitmessage_ar.ts \ From f4861be976c84a408fae1867e65128e35b102112 Mon Sep 17 00:00:00 2001 From: Dmitri Bogomolov <4glitch@gmail.com> Date: Wed, 31 Jan 2018 12:33:43 +0200 Subject: [PATCH 355/407] Add Ukrainian translation file --- src/translations/bitmessage.pro | 1 + 1 file changed, 1 insertion(+) diff --git a/src/translations/bitmessage.pro b/src/translations/bitmessage.pro index e3c85535..f5d6b946 100644 --- a/src/translations/bitmessage.pro +++ b/src/translations/bitmessage.pro @@ -68,6 +68,7 @@ TRANSLATIONS = \ bitmessage_pt.ts \ bitmessage_sk.ts \ bitmessage_ru.ts \ + bitmessage_uk.ts \ bitmessage_zh_cn.ts CODECFORTR = UTF-8 From 0c4ea7d20f6cb07e11bbe70e353ed4b336e35af5 Mon Sep 17 00:00:00 2001 From: PyBitmessage Translations Date: Wed, 31 Jan 2018 17:34:38 +0100 Subject: [PATCH 356/407] Auto-updated language ru from transifex --- src/translations/bitmessage_ru.qm | Bin 94216 -> 91386 bytes src/translations/bitmessage_ru.ts | 653 ++++++++++++++++-------------- 2 files changed, 341 insertions(+), 312 deletions(-) diff --git a/src/translations/bitmessage_ru.qm b/src/translations/bitmessage_ru.qm index bd61a1fa8c6b498c84f83d81d39131e62154eabf..c831d389433262f7920f31b7e6fd20c4e0ba7586 100644 GIT binary patch delta 4118 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 delta 5558 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 MW_w$9XE(b252=$Q$N&HU diff --git a/src/translations/bitmessage_ru.ts b/src/translations/bitmessage_ru.ts index 3e020294..38e1112f 100644 --- a/src/translations/bitmessage_ru.ts +++ b/src/translations/bitmessage_ru.ts @@ -2,17 +2,17 @@ AddAddressDialog - + Add new entry Добавить новую запись - + Label Имя - + Address Адрес @@ -20,64 +20,93 @@ EmailGatewayDialog - + Email gateway Email-шлюз - + Register on email gateway Зарегистрироваться на Email-шлюзе - + Account status at email gateway Статус аккаунта Email-шлюза - + Change account settings at email gateway Изменить настройки аккаунта Email-шлюза - + Unregister from email gateway Отменить регистрацию на Email-шлюзе - + Email gateway allows you to communicate with email users. Currently, only the Mailchuck email gateway (@mailchuck.com) is available. - Email-шлюз позволяет вам обмениваться сообщениями с пользователями обычной электронной почты. В настоящий момент доступен только 1 шлюз Mailchuck (@mailchuck.com). + Email-шлюз позволяет вам обмениваться сообщениями с пользователями обычной электронной почты. В настоящий момент доступен только шлюз Mailchuck (@mailchuck.com). - + Desired email address (including @mailchuck.com): - Введите желаемый email-адрес (включая @mailchuck.com) + Желаемый email-адрес (включая @mailchuck.com) + + + + @mailchuck.com + @mailchuck.com + + + + Registration failed: + Регистрация не удалась: + + + + The requested email address is not available, please try a new one. + Запрашиваемый адрес email недоступен, попробуйте ввести другой. + + + + Sending email gateway registration request + Отправка запроса на регистрацию на Email-шлюзе + + + + Sending email gateway unregistration request + Отправка запроса на отмену регистрации на Email-шлюзе + + + + Sending email gateway status request + Отправка запроса статуса аккаунта на Email-шлюзе EmailGatewayRegistrationDialog - + Registration failed: - Регистрация не удалась: + - + The requested email address is not available, please try a new one. Fill out the new desired email address (including @mailchuck.com) below: - Запрашиваемый адрес email недоступен, попробуйте ввести другой. Введите желаемый адрес (включая @mailchuck.com) ниже: + Email gateway registration - Регистрация на Email-шлюзе + Email gateway allows you to communicate with email users. Currently, only the Mailchuck email gateway (@mailchuck.com) is available. Please type the desired email address (including @mailchuck.com) below: - Email-шлюз позволяет вам обмениваться сообщениями с пользователями обычной электронной почты. В настоящий момент доступен только шлюз Mailchuck (@mailchuck.com). -Пожалуйста, введите желаемый адрес email (включая @mailchuck.com) ниже: + @@ -168,52 +197,52 @@ Please type the desired email address (including @mailchuck.com) below: MainWindow - + Reply to sender Ответить отправителю - + Reply to channel Ответить в канал - + Add sender to your Address Book Добавить отправителя в адресную книгу - + Add sender to your Blacklist Добавить отправителя в чёрный список - + Move to Trash Поместить в корзину - + Undelete Отменить удаление - + View HTML code as formatted text Просмотреть HTML код как отформатированный текст - + Save message as... Сохранить сообщение как ... - + Mark Unread Отметить как непрочитанное - + New Новый адрес @@ -238,12 +267,12 @@ Please type the desired email address (including @mailchuck.com) below: Скопировать адрес в буфер обмена - + Special address behavior... Особое поведение адресов... - + Email gateway Email-шлюз @@ -253,37 +282,37 @@ Please type the desired email address (including @mailchuck.com) below: Удалить - + Send message to this address Отправить сообщение на этот адрес - + Subscribe to this address Подписаться на рассылку с этого адреса - + Add New Address Добавить новый адрес - + Copy destination address to clipboard Скопировать адрес отправки в буфер обмена - + Force send Форсировать отправку - + One of your addresses, %1, is an old version 1 address. Version 1 addresses are no longer supported. May we delete it now? Один из Ваших адресов, %1, является устаревшим адресом версии 1. Адреса версии 1 больше не поддерживаются. Хотите ли Вы удалить его сейчас? - + Waiting for their encryption key. Will request it again soon. Ожидаем ключ шифрования от Вашего собеседника. Запрос будет повторен через некоторое время. @@ -293,17 +322,17 @@ Please type the desired email address (including @mailchuck.com) below: - + Queued. В очереди. - + Message sent. Waiting for acknowledgement. Sent at %1 Сообщение отправлено. Ожидаем подтверждения. Отправлено в %1 - + Message sent. Sent at %1 Сообщение отправлено в %1 @@ -313,78 +342,78 @@ Please type the desired email address (including @mailchuck.com) below: - + Acknowledgement of the message received %1 Доставлено в %1 - + Broadcast queued. Рассылка ожидает очереди. - + Broadcast on %1 Рассылка на %1 - + Problem: The work demanded by the recipient is more difficult than you are willing to do. %1 Проблема: Ваш получатель требует более сложных вычислений, чем максимум, указанный в Ваших настройках. %1 - + Problem: The recipient's encryption key is no good. Could not encrypt message. %1 Проблема: ключ получателя неправильный. Невозможно зашифровать сообщение. %1 - + Forced difficulty override. Send should start soon. Форсирована смена сложности. Отправляем через некоторое время. - + Unknown status: %1 %2 Неизвестный статус: %1 %2 - + Not Connected Не соединено - + Show Bitmessage Показать Bitmessage - + Send Отправить - + Subscribe Подписки - + Channel Канал - + Quit Выйти - + You may manage your keys by editing the keys.dat file stored in the same directory as this program. It is important that you back up this file. Вы можете управлять Вашими ключами, редактируя файл keys.dat, находящийся в той же папке, что и эта программа. Создайте резервную копию этого файла перед тем как будете его редактировать. - + You may manage your keys by editing the keys.dat file stored in %1 It is important that you back up this file. @@ -393,19 +422,19 @@ It is important that you back up this file. Создайте резервную копию этого файла перед тем как будете его редактировать. - + Open keys.dat? Открыть файл keys.dat? - + You may manage your keys by editing the keys.dat file stored in the same directory as this program. It is important that you back up this file. Would you like to open the file now? (Be sure to close Bitmessage before making any changes.) Вы можете управлять Вашими ключами, редактируя файл keys.dat, находящийся в той же папке, что и эта программа. Создайте резервную копию этого файла перед тем как будете его редактировать. Хотели бы Вы открыть этот файл сейчас? (пожалуйста, закройте Bitmessage до того как Вы внесёте в этот файл какие-либо изменения.) - + You may manage your keys by editing the keys.dat file stored in %1 It is important that you back up this file. Would you like to open the file now? (Be sure to close Bitmessage before making any changes.) @@ -415,27 +444,27 @@ It is important that you back up this file. Would you like to open the file now? (пожалуйста, закройте Bitmessage до того как Вы внесёте в этот файл какие-либо изменения.) - + Delete trash? Очистить корзину? - + Are you sure you want to delete all trashed messages? Вы уверены что хотите очистить корзину? - + bad passphrase Неподходящая секретная фраза - + You must type your passphrase. If you don't have one then this is not the form for you. Вы должны ввести секретную фразу. Если Вы не хотите этого делать, то Вы выбрали неправильную опцию. - + Bad address version number Неверный номер версии адреса @@ -445,7 +474,7 @@ It is important that you back up this file. Would you like to open the file now? Адрес номера версии должен быть числом: либо 3, либо 4. - + Your address version number must be either 3 or 4. Адрес номера версии должен быть либо 3, либо 4. @@ -515,22 +544,22 @@ It is important that you back up this file. Would you like to open the file now? - + Connection lost Соединение потеряно - + Connected Соединено - + Message trashed Сообщение удалено - + The TTL, or Time-To-Live is the length of time that the network will hold the message. The recipient must get it during this time. If your Bitmessage client does not hear an acknowledgement, it will resend the message automatically. The longer the Time-To-Live, the @@ -541,17 +570,17 @@ It is important that you back up this file. Would you like to open the file now? сообщение. Часто разумным вариантом будет установка TTL на 4 или 5 дней. - + Message too long Сообщение слишком длинное - + The message that you are trying to send is too long by %1 bytes. (The maximum is 261644 bytes). Please cut it down before sending. Сообщение, которое вы пытаетесь отправить, длиннее максимально допустимого на %1 байт. (Максимально допустимое значение 261644 байта). Пожалуйста, сократите сообщение перед отправкой. - + Error: Your account wasn't registered at an email gateway. Sending registration now as %1, please wait for the registration to be processed before retrying sending. Ошибка: ваш аккаунт не зарегистрирован на Email-шлюзе. Отправка регистрации %1, пожалуйста, подождите пока процесс регистрации не завершится, прежде чем попытаться отправить сообщение заново. @@ -596,57 +625,57 @@ It is important that you back up this file. Would you like to open the file now? - + Error: You must specify a From address. If you don't have one, go to the 'Your Identities' tab. Вы должны указать адрес в поле "От кого". Вы можете найти Ваш адрес во вкладке "Ваши Адреса". - + Address version number Версия адреса - + Concerning the address %1, Bitmessage cannot understand address version numbers of %2. Perhaps upgrade Bitmessage to the latest version. По поводу адреса %1: Bitmessage не поддерживаем адреса версии %2. Возможно, Вам нужно обновить клиент Bitmessage. - + Stream number Номер потока - + Concerning the address %1, Bitmessage cannot handle stream numbers of %2. Perhaps upgrade Bitmessage to the latest version. По поводу адреса %1: Bitmessage не поддерживаем стрим номер %2. Возможно, Вам нужно обновить клиент Bitmessage. - + Warning: You are currently not connected. Bitmessage will do the work necessary to send the message but it won't send until you connect. Внимание: Вы не подключены к сети. Bitmessage выполнит работу, требуемую для отправки сообщения, но не отправит его до тех пор, пока Вы не подключитесь. - + Message queued. Сообщение в очереди. - + Your 'To' field is empty. Вы не заполнили поле 'Кому'. - + Right click one or more entries in your address book and select 'Send message to this address'. Нажмите правую кнопку мыши на каком-либо адресе и выберите "Отправить сообщение на этот адрес". - + Fetched address from namecoin identity. Получить адрес через Namecoin. - + New Message Новое сообщение @@ -656,9 +685,9 @@ It is important that you back up this file. Would you like to open the file now? - + Sending email gateway registration request - Отправка запроса на регистрацию на Email-шлюзе + @@ -671,142 +700,142 @@ It is important that you back up this file. Would you like to open the file now? Вы ввели неправильный адрес. Это адрес проигнорирован. - + Error: You cannot add the same address to your address book twice. Try renaming the existing one if you want. Ошибка: Вы не можете добавлять один и тот же адрес в Адресную Книгу несколько раз. Попробуйте переименовать существующий адрес. - + Error: You cannot add the same address to your subscriptions twice. Perhaps rename the existing one if you want. Ошибка: вы не можете добавить один и тот же адрес в ваши подписки дважды. Пожалуйста, переименуйте имеющийся адрес, если хотите. - + Restart Перезапустить - + You must restart Bitmessage for the port number change to take effect. Вы должны перезапустить Bitmessage, чтобы смена номера порта имела эффект. - + Bitmessage will use your proxy from now on but you may want to manually restart Bitmessage now to close existing connections (if any). Bitmessage будет использовать Ваш прокси, начиная прямо сейчас. Тем не менее Вам имеет смысл перезапустить Bitmessage, чтобы закрыть уже существующие соединения. - + Number needed Требуется число - + Your maximum download and upload rate must be numbers. Ignoring what you typed. Скорости загрузки и выгрузки должны быть числами. Игнорирую то, что вы набрали. - + Will not resend ever Не пересылать никогда - + Note that the time limit you entered is less than the amount of time Bitmessage waits for the first resend attempt therefore your messages will never be resent. Обратите внимание, что лимит времени, который вы ввели, меньше чем время, которое Bitmessage ждет перед первой попыткой переотправки сообщения, поэтому ваши сообщения никогда не будут переотправлены. - + Sending email gateway unregistration request - Отправка запроса на отмену регистрации на Email-шлюзе + - + Sending email gateway status request - Отправка запроса статуса аккаунта на Email-шлюзе + - + Passphrase mismatch Секретная фраза не подходит - + The passphrase you entered twice doesn't match. Try again. Вы ввели две разные секретные фразы. Пожалуйста, повторите заново. - + Choose a passphrase Придумайте секретную фразу - + You really do need a passphrase. Вы действительно должны ввести секретную фразу. - + Address is gone Адрес утерян - + Bitmessage cannot find your address %1. Perhaps you removed it? Bitmessage не может найти Ваш адрес %1. Возможно Вы удалили его? - + Address disabled Адрес выключен - + Error: The address from which you are trying to send is disabled. You'll have to enable it on the 'Your Identities' tab before using it. Ошибка: адрес, с которого Вы пытаетесь отправить, выключен. Вам нужно будет включить этот адрес во вкладке "Ваши адреса". - + Entry added to the Address Book. Edit the label to your liking. - Запись добавлена в Адресную Книгу. Вы можете её отредактировать. + - + Entry added to the blacklist. Edit the label to your liking. Запись добавлена в чёрный список. Измените название по своему вкусу. - + Error: You cannot add the same address to your blacklist twice. Try renaming the existing one if you want. Ошибка: вы не можете добавить один и тот же адрес в чёрный список дважды. Попробуйте переименовать существующий адрес. - + Moved items to trash. Удалено в корзину. - + Undeleted item. - + Элемент восстановлен. - + Save As... Сохранить как ... - + Write error. Ошибка записи. - + No addresses selected. Вы не выбрали адрес. - + If you delete the subscription, messages that you already received will become inaccessible. Maybe you can consider disabling the subscription instead. Disabled subscriptions will not receive new messages, but you can still view messages you already received. Are you sure you want to delete the subscription? @@ -815,7 +844,7 @@ Are you sure you want to delete the subscription? Вы уверены, что хотите отменить подписку? - + If you delete the channel, messages that you already received will become inaccessible. Maybe you can consider disabling the channel instead. Disabled channels will not receive new messages, but you can still view messages you already received. Are you sure you want to delete the channel? @@ -824,277 +853,277 @@ Are you sure you want to delete the channel? Вы уверены, что хотите удалить канал? - + Do you really want to remove this avatar? Вы уверены, что хотите удалить этот аватар? - + You have already set an avatar for this address. Do you really want to overwrite it? У вас уже есть аватар для этого адреса. Вы уверены, что хотите перезаписать аватар? - + Start-on-login not yet supported on your OS. Запуск программы при входе в систему ещё не поддерживается в вашей операционной системе. - + Minimize-to-tray not yet supported on your OS. Сворачивание в трей ещё не поддерживается в вашей операционной системе. - + Tray notifications not yet supported on your OS. Уведомления в трее ещё не поддерживаеются в вашей операционной системе. - + Testing... Проверяем... - + This is a chan address. You cannot use it as a pseudo-mailing list. - Это адрес chan-а. Вы не можете его использовать как адрес рассылки. + - + The address should start with ''BM-'' Адрес должен начинаться с "BM-" - + The address is not typed or copied correctly (the checksum failed). Адрес введен или скопирован неверно (контрольная сумма не сходится). - + The version number of this address is higher than this software can support. Please upgrade Bitmessage. - Версия этого адреса более поздняя, чем Ваша программа. Пожалуйста, обновите программу Bitmessage. + Версия этого адреса более поздняя, чем те, что поддерживает программа. Пожалуйста, обновите Bitmessage. - + The address contains invalid characters. Адрес содержит запрещённые символы. - + Some data encoded in the address is too short. Данные, закодированные в адресе, слишком короткие. - + Some data encoded in the address is too long. Данные, закодированные в адресе, слишком длинные. - + Some data encoded in the address is malformed. Данные, закодированные в адресе, имеют неверный формат. - + Enter an address above. - Введите адрес выше. + - + Address is an old type. We cannot display its past broadcasts. Адрес старого типа. Мы не можем отобразить его прошлые рассылки. - + There are no recent broadcasts from this address to display. Нет недавних рассылок с этого адреса для отображения. - + You are using TCP port %1. (This can be changed in the settings). - Вы используете TCP порт %1. (Его можно поменять в настройках). + - + Bitmessage Bitmessage - + Identities Адреса - + New Identity Создать новый адрес - + Search Поиск - + All По всем полям - + To Кому - + From От кого - + Subject Тема - + Message Текст сообщения - + Received Получено - + Messages Сообщения - + Address book Адресная книга - + Address Адрес - + Add Contact Добавить контакт - + Fetch Namecoin ID Получить Namecoin ID - + Subject: Тема: - + From: От: - + To: Кому: - + Send ordinary Message Отправить обычное сообщение - + Send Message to your Subscribers Отправить сообщение для ваших подписчиков - + TTL: TTL: - + Subscriptions Подписки - + Add new Subscription Добавить новую подписку - + Chans Чаны - + Add Chan Добавить чан - + File Файл - + Settings Настройки - + Help Помощь - + Import keys Импортировать ключи - + Manage keys Управлять ключами - + Ctrl+Q Ctrl+Q - + F1 F1 - + Contact support Связаться с поддержкой - + About О программе - + Regenerate deterministic addresses Сгенерировать заново все адреса - + Delete all trashed messages Стереть все сообщения из корзины - + Join / Create chan Подключить или создать чан @@ -1119,67 +1148,67 @@ Are you sure you want to delete the channel? Добавить новую запись - + Display the %1 recent broadcast(s) from this address. Показать %1 прошлых рассылок с этого адреса. - + New version of PyBitmessage is available: %1. Download it from https://github.com/Bitmessage/PyBitmessage/releases/latest Доступна новая версия PyBitmessage: %1. Загрузите её: https://github.com/Bitmessage/PyBitmessage/releases/latest - + Waiting for PoW to finish... %1% Ожидание окончания PoW... %1% - + Shutting down Pybitmessage... %1% Завершение PyBitmessage... %1% - + Waiting for objects to be sent... %1% Ожидание отправки объектов... %1% - + Saving settings... %1% Сохранение настроек... %1% - + Shutting down core... %1% Завершение работы ядра... %1% - + Stopping notifications... %1% Остановка сервиса уведомлений... %1% - + Shutdown imminent... %1% Завершение вот-вот произойдет... %1% - + %n hour(s) %n час%n часа%n часов%n час(а/ов) - + %n day(s) %n день%n дня%n дней%n дней - + Shutting down PyBitmessage... %1% Завершение PyBitmessage... %1% - + Sent Отправлено @@ -1283,7 +1312,7 @@ Receiver's required difficulty: %1 and %2 Проблема: вы пытаетесь отправить сообщение самому себе или в чан, но ваш ключ шифрования не найден в файле ключей keys.dat. Невозможно зашифровать сообщение. %1 - + Doing work necessary to send message. Выполнение работы, требуемой для отправки сообщения. @@ -1293,7 +1322,7 @@ Receiver's required difficulty: %1 and %2 Отправлено. Ожидаем подтверждения. Отправлено в %1 - + Doing work necessary to request encryption key. Выполнение работы, требуемой для запроса ключа шифрования. @@ -1318,37 +1347,37 @@ Receiver's required difficulty: %1 and %2 Распределение портов UPnP отменено - + Mark all messages as read Отметить все сообщения как прочтенные - + Are you sure you would like to mark all messages read? Вы уверены, что хотите отметить все сообщения как прочтенные? - + Doing work necessary to send broadcast. Выполнение работы, требуемой для отправки рассылки. - + Proof of work pending Ожидается доказательство работы - + %n object(s) pending proof of work %n объект в ожидании доказательства работы%n объекта в ожидании доказательства работы%n объектов в ожидании доказательства работы%n объектов в ожидании доказательства работы - + %n object(s) waiting to be distributed %n объект ожидает раздачи%n объекта ожидают раздачи%n объектов ожидают раздачи%n объектов ожидают раздачи - + Wait until these tasks finish? Подождать завершения этих задач? @@ -1413,12 +1442,12 @@ Receiver's required difficulty: %1 and %2 Ваша видеокарта вычислила неправильно, отключаем OpenCL. Пожалуйста, сообщите разработчикам. - + Set notification sound... Установить звук уведомления... - + Welcome to easy and secure Bitmessage * send messages to other people @@ -1432,145 +1461,135 @@ Receiver's required difficulty: %1 and %2 * участвуйте в обсуждениях в чанах - + not recommended for chans не рекомендовано для чанов - + Quiet Mode Тихий режим - + Problems connecting? Try enabling UPnP in the Network Settings Проблемы подключения? Попробуйте включить UPnP в сетевых настройках. - + You are trying to send an email instead of a bitmessage. This requires registering with a gateway. Attempt to register? Вы пытаетесь отправить email вместо bitmessage. Для этого нужно зарегистрироваться на шлюзе. Попробовать зарегистрироваться? - + Error: Bitmessage addresses start with BM- Please check the recipient address %1 Ошибка: адреса Bitmessage начинаются с "BM-". Пожалуйста, проверьте адрес получателя %1. - + Error: The recipient address %1 is not typed or copied correctly. Please check it. Ошибка: адрес получателя %1 набран или скопирован неправильно. Пожалуйста, проверьте его. - + Error: The recipient address %1 contains invalid characters. Please check it. Ошибка: адрес получателя %1 содержит недопустимые символы. Пожалуйста, проверьте его. - + Error: The version of the recipient address %1 is too high. Either you need to upgrade your Bitmessage software or your acquaintance is being clever. Ошибка: версия адреса получателя %1 слишком высокая. Либо вам нужно обновить программу Bitmessage, либо ваш знакомый - умник. - + Error: Some data encoded in the recipient address %1 is too short. There might be something wrong with the software of your acquaintance. Ошибка: часть данных, закодированных в адресе получателя %1 слишком короткая. Видимо, что-то не так с программой, используемой вашим знакомым. - + Error: Some data encoded in the recipient address %1 is too long. There might be something wrong with the software of your acquaintance. Ошибка: часть данных, закодированных в адресе получателя %1 слишком длинная. Видимо, что-то не так с программой, используемой вашим знакомым. - + Error: Some data encoded in the recipient address %1 is malformed. There might be something wrong with the software of your acquaintance. Ошибка: часть данных, закодированных в адресе получателя %1 сформирована неправильно. Видимо, что-то не так с программой, используемой вашим знакомым. - + Error: Something is wrong with the recipient address %1. Ошибка: что-то не так с адресом получателя %1. - - Error: %1 - Ошибка: %1 - - - + From %1 От %1 - + Synchronisation pending Ожидается синхронизация - + Bitmessage hasn't synchronised with the network, %n object(s) to be downloaded. If you quit now, it may cause delivery delays. Wait until the synchronisation finishes? Bitmessage не синхронизирован с сетью, незагруженных объектов: %n. Выход сейчас может привести к задержкам доставки. Подождать завершения синхронизации?Bitmessage не синхронизирован с сетью, незагруженных объектов: %n. Выход сейчас может привести к задержкам доставки. Подождать завершения синхронизации?Bitmessage не синхронизирован с сетью, незагруженных объектов: %n. Выход сейчас может привести к задержкам доставки. Подождать завершения синхронизации?Bitmessage не синхронизирован с сетью, незагруженных объектов: %n. Выход сейчас может привести к задержкам доставки. Подождать завершения синхронизации? - + Not connected Не подключено - + Bitmessage isn't connected to the network. If you quit now, it may cause delivery delays. Wait until connected and the synchronisation finishes? Bitmessage не подключен к сети. Выход сейчас может привести к задержкам доставки. Подождать подключения и завершения синхронизации? - + Waiting for network connection... Ожидание сетевого подключения... - + Waiting for finishing synchronisation... Ожидание окончания синхронизации... - - Undeleted items. - Восстановленные элементы. - - - + You have already set a notification sound for this address book entry. Do you really want to overwrite it? У вас уже есть звук уведомления для этого адресата. Вы уверены, что хотите перезаписать звук уведомления? - + Go online Подключиться к сети - + Go offline - + Отключиться от сети MessageView - + Follow external link Перейти по внешней ссылке - + The link "%1" will open in a browser. It may be a security risk, it could de-anonymise you or download malicious data. Are you sure? Ссылка "%1" откроется в браузере. Это может быть угрозой безопасности, например деанонимизировать вас или привести к скачиванию вредоносных данных. Вы уверены? - + HTML detected, click here to display Обнаружен HTML, нажмите здесь чтоб отобразить - + Click here to disable HTML Нажмите здесь для отключения @@ -1593,98 +1612,98 @@ Perhaps you should upgrade Bitmessage. NewAddressDialog - + Create new Address Создать новый адрес - + Here you may generate as many addresses as you like. Indeed, creating and abandoning addresses is encouraged. You may generate addresses by using either random numbers or by using a passphrase. If you use a passphrase, the address is called a "deterministic" address. The 'Random Number' option is selected by default but deterministic addresses have several pros and cons: Здесь Вы сможете сгенерировать столько адресов сколько хотите. На самом деле, создание и выкидывание адресов даже поощряется. Вы можете сгенерировать адреса используя либо генератор случайных чисел, либо придумав секретную фразу. Если Вы используете секретную фразу, то адреса будут называться "детерминистическими". Генератор случайных чисел выбран по умолчанию, однако детерминистические адреса имеют следующие плюсы и минусы по сравнению с ними: - + <html><head/><body><p><span style=" font-weight:600;">Pros:<br/></span>You can recreate your addresses on any computer from memory. <br/>You need-not worry about backing up your keys.dat file as long as you can remember your passphrase. <br/><span style=" font-weight:600;">Cons:<br/></span>You must remember (or write down) your passphrase if you expect to be able to recreate your keys if they are lost. <br/>You must remember the address version number and the stream number along with your passphrase. <br/>If you choose a weak passphrase and someone on the Internet can brute-force it, they can read your messages and send messages as you.</p></body></html> <html><head/><body><p><span style=" font-weight:600;">Плюсы:<br/></span>Вы сможете восстановить адрес по памяти на любом компьютере<br/>Вам не нужно беспокоиться о сохранении файла keys.dat, если Вы запомнили секретную фразу<br/><span style=" font-weight:600;">Минусы:<br/></span>Вы должны запомнить (или записать) секретную фразу, если Вы хотите когда-либо восстановить Ваш адрес на другом компьютере <br/>Вы должны также запомнить версию адреса и номер потока вместе с секретной фразой<br/>Если Вы выберите слишком короткую секретную фразу, кто-нибудь в интернете сможет подобрать ключ и, как следствие, читать и отправлять от Вашего имени сообщения.</p></body></html> - + Use a random number generator to make an address Использовать генератор случайных чисел для создания адреса - + Use a passphrase to make addresses Использовать секретную фразу для создания адресов - + Spend several minutes of extra computing time to make the address(es) 1 or 2 characters shorter Потратить несколько лишних минут, чтобы сделать адрес(а) короче на 1 или 2 символа - + Make deterministic addresses - Создать детерминистический адрес + Создать детерминистические адреса - + Address version number: 4 Номер версии адреса: 4 - + In addition to your passphrase, you must remember these numbers: В дополнение к секретной фразе, Вам необходимо запомнить эти числа: - + Passphrase - Придумайте секретную фразу + Секретная фраза - + Number of addresses to make based on your passphrase: - Кол-во адресов, которые Вы хотите получить из секретной фразы: + Количество адресов, которые нужно создать из секретной фразы: - + Stream number: 1 Номер потока: 1 - + Retype passphrase Повторите секретную фразу - + Randomly generate address Сгенерировать случайный адрес - + Label (not shown to anyone except you) Имя (не показывается никому кроме Вас) - + Use the most available stream Использовать наиболее доступный поток - + (best if this is the first of many addresses you will create) (выберите этот вариант если это лишь первый из многих адресов, которые Вы планируете создать) - + Use the same stream as an existing address Использовать тот же поток, что и указанный существующий адрес - + (saves you some bandwidth and processing power) (немного сэкономит Вам пропускную способность сети и вычислительную мощь) @@ -1692,22 +1711,22 @@ The 'Random Number' option is selected by default but deterministic ad NewSubscriptionDialog - + Add new entry Добавить новую запись - + Label Имя - + Address Адрес - + Enter an address above. Введите адрес выше. @@ -1715,55 +1734,60 @@ The 'Random Number' option is selected by default but deterministic ad SpecialAddressBehaviorDialog - + Special Address Behavior Особое поведение адреса - + Behave as a normal address Вести себя как обычный адрес - + Behave as a pseudo-mailing-list address Вести себя как адрес псевдо-рассылки - + Mail received to a pseudo-mailing-list address will be automatically broadcast to subscribers (and thus will be public). - Почта, полученная на адрес псевдо-рассылки, будет автоматически разослана всем подписчикам (и поэтому будет доступна общей публике). + Почта, полученная на адрес псевдо-рассылки, будет автоматически разослана всем подписчикам (и поэтому будет публичной). - + Name of the pseudo-mailing-list: Имя псевдо-рассылки: + + + This is a chan address. You cannot use it as a pseudo-mailing list. + Это адрес чана. Вы не можете его использовать как адрес рассылки. + aboutDialog - + About О программе - + PyBitmessage - PyBitmessage + - + version ? - версия ? + - + <html><head/><body><p>Distributed under the MIT/X11 software license; see <a href="http://www.opensource.org/licenses/mit-license.php"><span style=" text-decoration: underline; color:#0000ff;">http://www.opensource.org/licenses/mit-license.php</span></a></p></body></html> <html><head/><body><p>Программа распространяется в соответствии с лицензией MIT/X11; см. <a href="http://www.opensource.org/licenses/mit-license.php"><span style=" text-decoration: underline; color:#0000ff;">http://www.opensource.org/licenses/mit-license.php</span></a></p></body></html> - + This is Beta software. Это бета версия программы. @@ -1772,10 +1796,10 @@ The 'Random Number' option is selected by default but deterministic ad <html><head/><body><p>Copyright © 2012-2016 Jonathan Warren<br/>Copyright © 2013-2016 The Bitmessage Developers</p></body></html> - - - <html><head/><body><p>Copyright &copy; 2012-2016 Jonathan Warren<br/>Copyright &copy; 2013-2016 The Bitmessage Developers</p></body></html> - <html><head/><body><p>Авторское право: &copy; 2012-2016 Джонатан Уоррен<br/>Авторское право: &copy; 2013-2016 Разработчики Bitmessage</p></body></html> + + + <html><head/><body><p>Copyright © 2012-2016 Jonathan Warren<br/>Copyright © 2013-2017 The Bitmessage Developers</p></body></html> + <html><head/><body><p>Авторское право: &copy; 2012-2016 Джонатан Уоррен<br/>Авторское право: &copy; 2013-2017 Разработчики Bitmessage</p></body></html> @@ -1819,45 +1843,45 @@ The 'Random Number' option is selected by default but deterministic ad connectDialog - + Bitmessage Bitmessage - + Bitmessage won't connect to anyone until you let it. Bitmessage не будет соединяться ни с кем, пока Вы это не разрешите. - + Connect now Соединиться прямо сейчас - + Let me configure special network settings first Я хочу сперва настроить сетевые настройки - + Work offline - + Работать без соединения с сетью helpDialog - + Help Помощь - + <a href="https://bitmessage.org/wiki/PyBitmessage_Help">https://bitmessage.org/wiki/PyBitmessage_Help</a> <a href="https://bitmessage.org/wiki/PyBitmessage_Help">https://bitmessage.org/wiki/PyBitmessage_Help</a> - + As Bitmessage is a collaborative project, help can be found online in the Bitmessage Wiki: Bitmessage - общественный проект. Вы можете найти подсказки и советы на Wiki-страничке Bitmessage: @@ -1865,30 +1889,35 @@ The 'Random Number' option is selected by default but deterministic ad iconGlossaryDialog - + Icon Glossary Описание значков - + You have no connections with other peers. Нет соединения с другими участниками сети. - + You have made at least one connection to a peer using an outgoing connection but you have not yet received any incoming connections. Your firewall or home router probably isn't configured to forward incoming TCP connections to your computer. Bitmessage will work just fine but it would help the Bitmessage network if you allowed for incoming connections and will help you be a better-connected node. - На текущий момент Вы установили по-крайней мере одно исходящее соединение, но пока ни одного входящего. Ваш файрвол или маршрутизатор скорее всего не настроен на переброс входящих TCP соединений к Вашему компьютеру. Bitmessage будет прекрасно работать и без этого, но Вы могли бы помочь сети если бы разрешили и входящие соединения тоже. Это помогло бы Вам стать более важным узлом сети. + Вы установили по-крайней мере одно исходящее соединение, но пока ни одного входящего. Ваш файрвол или маршрутизатор скорее всего не настроен на переброс входящих TCP соединений к Вашему компьютеру. Bitmessage будет прекрасно работать и без этого, но Вы могли бы помочь сети если бы разрешили и входящие соединения тоже. Это помогло бы Вам стать более важным узлом сети. You are using TCP port ?. (This can be changed in the settings). - Вы используете TCP порт ?. (Его можно поменять в настройках). + - + You do have connections with other peers and your firewall is correctly configured. Вы установили соединение с другими участниками сети и ваш файрвол настроен правильно. + + + You are using TCP port %1. (This can be changed in the settings). + Вы используете TCP порт %1. (Его можно поменять в настройках). + networkstatus @@ -1973,7 +2002,7 @@ The 'Random Number' option is selected by default but deterministic ad Загрузка: 0 кБ/с - + Network Status Состояние сети @@ -2129,12 +2158,12 @@ The 'Random Number' option is selected by default but deterministic ad Успешно создан / подключен чан %1 - + Chan creation / joining failed Не удалось создать / подключить чан - + Chan creation / joining cancelled Создание / подключение чана отменено @@ -2168,54 +2197,54 @@ The 'Random Number' option is selected by default but deterministic ad regenerateAddressesDialog - + Regenerate Existing Addresses Сгенерировать заново существующие адреса - + Regenerate existing addresses Сгенерировать заново существующие адреса - + Passphrase Секретная фраза - + Number of addresses to make based on your passphrase: - Кол-во адресов, которые Вы хотите получить из Вашей секретной фразы: + Количество адресов, которые нужно создать из секретной фразы: - + Address version number: Номер версии адреса: - + Stream number: Номер потока: - + 1 1 - + Spend several minutes of extra computing time to make the address(es) 1 or 2 characters shorter Потратить несколько лишних минут, чтобы сделать адрес(а) короче на 1 или 2 символа - + You must check (or not check) this box just like you did (or didn't) when you made your addresses the first time. - Вы должны отметить эту галочку (или не отмечать) точно так же, как Вы сделали (или не сделали) в самый первый раз, когда создавали Ваши адреса. + - + If you have previously made deterministic addresses but lost them due to an accident (like hard drive failure), you can regenerate them here. If you used the random number generator to make your addresses then this form will be of no use to you. - Если Вы ранее создавали детерминистические адреса, но случайно потеряли их, Вы можете их восстановить здесь. Если же Вы использовали генератор случайных чисел чтобы создать Ваши адреса, то Вы не сможете их здесь восстановить. + From 25a9e5ea450628ac5141c804afceb413f35acbc8 Mon Sep 17 00:00:00 2001 From: Dmitri Bogomolov <4glitch@gmail.com> Date: Wed, 31 Jan 2018 20:12:04 +0200 Subject: [PATCH 357/407] Fixed errors I introduced in commit 4115540 after rebase --- src/bitmessageqt/__init__.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/bitmessageqt/__init__.py b/src/bitmessageqt/__init__.py index 8e836fe1..3422664b 100644 --- a/src/bitmessageqt/__init__.py +++ b/src/bitmessageqt/__init__.py @@ -1904,7 +1904,7 @@ class MyForm(settingsmixin.SMainWindow): " BM- Please check the recipient address %1" ).arg(toAddress)) elif status == 'checksumfailed': - self.statusbar_message(_translate( + self.updateStatusBar(_translate( "MainWindow", "Error: The recipient address %1 is not" " typed or copied correctly. Please check it." @@ -2095,7 +2095,8 @@ class MyForm(settingsmixin.SMainWindow): identities = str(self.ui.lineEditTo.text().toUtf8()).split(";") err, addr = nc.query(identities[-1].strip()) if err is not None: - self.statusbar_message("Error: %1", args=[err]) + self.updateStatusBar( + _translate("MainWindow", "Error: %1").arg(err)) else: identities[-1] = addr self.ui.lineEditTo.setText("; ".join(identities)) From 222e666a607c9d88f389fbb422006f8b56205f89 Mon Sep 17 00:00:00 2001 From: Peter Surda Date: Wed, 31 Jan 2018 21:09:36 +0100 Subject: [PATCH 358/407] Network thread shutdown iterator fix Thanks to @g1itch for pointing this out. --- src/network/networkthread.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/network/networkthread.py b/src/network/networkthread.py index 25e9f547..5a709c8b 100644 --- a/src/network/networkthread.py +++ b/src/network/networkthread.py @@ -20,17 +20,17 @@ class BMNetworkThread(threading.Thread, StoppableThread): def stopThread(self): super(BMNetworkThread, self).stopThread() - for i in BMConnectionPool().listeningSockets: + for i in BMConnectionPool().listeningSockets.values(): try: i.close() except: pass - for i in BMConnectionPool().outboundConnections: + for i in BMConnectionPool().outboundConnections.values(): try: i.close() except: pass - for i in BMConnectionPool().inboundConnections: + for i in BMConnectionPool().inboundConnections.values(): try: i.close() except: From d223bfc6f29367b7d70e35f7bf1185a7bc3ca239 Mon Sep 17 00:00:00 2001 From: Peter Surda Date: Wed, 31 Jan 2018 22:25:23 +0100 Subject: [PATCH 359/407] Fix kqueue poller - separate read and write filters - make filters peristent for reduced syscall count --- src/network/asyncore_pollchoose.py | 95 +++++++++++++++++++++--------- 1 file changed, 68 insertions(+), 27 deletions(-) diff --git a/src/network/asyncore_pollchoose.py b/src/network/asyncore_pollchoose.py index f9dc9ee5..cd19063a 100644 --- a/src/network/asyncore_pollchoose.py +++ b/src/network/asyncore_pollchoose.py @@ -361,35 +361,65 @@ def kqueue_poller(timeout=0.0, map=None): """A poller which uses kqueue(), BSD specific.""" if map is None: map = socket_map + try: + kqueue_poller.pollster + except AttributeError: + kqueue_poller.pollster = select.kqueue() if map: - kqueue = select.kqueue() - flags = select.KQ_EV_ADD | select.KQ_EV_ENABLE + updates = [] selectables = 0 for fd, obj in map.items(): kq_filter = 0 if obj.readable(): - kq_filter |= select.KQ_FILTER_READ - if obj.writable(): - kq_filter |= select.KQ_FILTER_WRITE - if kq_filter: - try: - ev = select.kevent(fd, filter=kq_filter, flags=flags) - kqueue.control([ev], 0) - selectables += 1 - except IOError: - pass + kq_filter |= 1 + selectables += 1 + if obj.writable() and not obj.accepting: + kq_filter |= 2 + selectables += 1 + if kq_filter != obj.poller_filter: + # unlike other pollers, READ and WRITE aren't OR able but have + # to be set and checked separately + if kq_filter & 1 != obj.poller_filter & 1: + poller_flags = select.KQ_EV_ADD + if kq_filter & 1: + poller_flags |= select.KQ_EV_ENABLE + else: + poller_flags |= select.KQ_EV_DISABLE + updates.append(select.kevent(fd, filter=select.KQ_FILTER_READ, flags=poller_flags)) + if kq_filter & 2 != obj.poller_filter & 2: + poller_flags = select.KQ_EV_ADD + if kq_filter & 2: + poller_flags |= select.KQ_EV_ENABLE + else: + poller_flags |= select.KQ_EV_DISABLE + updates.append(select.kevent(fd, filter=select.KQ_FILTER_WRITE, flags=poller_flags)) + obj.poller_filter = kq_filter - events = kqueue.control(None, selectables, timeout) - for event in random.sample(events, len(events)): + if not selectables: + # unlike other pollers, kqueue poll does not wait if there are no + # filters setup + current_thread().stop.wait(timeout) + return + + events = kqueue_poller.pollster.control(updates, selectables, timeout) + if len(events) > 1: + events = random.sample(events, len(events)) + + for event in events: fd = event.ident obj = map.get(fd) if obj is None: continue + if event.flags & select.KQ_EV_ERROR: + _exception(obj) + continue + if event.flags & select.KQ_EV_EOF and event.data and event.fflags: + obj.handle_close() + continue if event.filter == select.KQ_FILTER_READ: read(obj) if event.filter == select.KQ_FILTER_WRITE: write(obj) - kqueue.close() else: current_thread().stop.wait(timeout) @@ -499,27 +529,38 @@ class dispatcher: map = self._map map[self._fileno] = self self.poller_flags = 0 + self.poller_filter = 0 def del_channel(self, map=None): fd = self._fileno if map is None: map = self._map - self.poller_flags = 0 - self.poller_registered = False if fd in map: #self.log_info('closing channel %d:%s' % (fd, self)) del map[fd] + if self._fileno: + try: + kqueue_poller.pollster.control([select.kevent(fd, select.KQ_FILTER_READ, select.KQ_EV_DELETE)], 0) + except (AttributeError, KeyError, TypeError, IOError, OSError): + pass + try: + kqueue_poller.pollster.control([select.kevent(fd, select.KQ_FILTER_WRITE, select.KQ_EV_DELETE)], 0) + except (AttributeError, KeyError, TypeError, IOError, OSError): + pass + try: + epoll_poller.pollster.unregister(fd) + except (AttributeError, KeyError, TypeError, IOError): + # no epoll used, or not registered + pass + try: + poll_poller.pollster.unregister(fd) + except (AttributeError, KeyError, TypeError, IOError): + # no poll used, or not registered + pass self._fileno = None - try: - epoll_poller.pollster.unregister(fd) - except (AttributeError, KeyError, TypeError, IOError): - # no epoll used, or not registered - pass - try: - poll_poller.pollster.unregister(fd) - except (AttributeError, KeyError, TypeError, IOError): - # no poll used, or not registered - pass + self.poller_flags = 0 + self.poller_filter = 0 + self.poller_registered = False def create_socket(self, family=socket.AF_INET, socket_type=socket.SOCK_STREAM): self.family_and_type = family, socket_type From 68b58ce0c5c288e964e0036aee284a50d51f0b5a Mon Sep 17 00:00:00 2001 From: Peter Surda Date: Thu, 1 Feb 2018 12:19:39 +0100 Subject: [PATCH 360/407] Download optimisation - new data structure to handle download tracking, uses less CPU --- src/network/bmproto.py | 3 +- src/network/connectionpool.py | 3 +- src/network/downloadthread.py | 29 ++------- src/network/objectracker.py | 12 ++-- src/randomtrackingdict.py | 118 ++++++++++++++++++++++++++++++++++ 5 files changed, 129 insertions(+), 36 deletions(-) create mode 100644 src/randomtrackingdict.py diff --git a/src/network/bmproto.py b/src/network/bmproto.py index 21ec692c..4bf63bca 100644 --- a/src/network/bmproto.py +++ b/src/network/bmproto.py @@ -546,8 +546,7 @@ class BMProto(AdvancedDispatcher, ObjectTracker): for connection in network.connectionpool.BMConnectionPool().inboundConnections.values() + \ network.connectionpool.BMConnectionPool().outboundConnections.values(): try: - with connection.objectsNewToMeLock: - del connection.objectsNewToMe[hashId] + del connection.objectsNewToMe[hashId] except KeyError: pass if not forwardAnyway: diff --git a/src/network/connectionpool.py b/src/network/connectionpool.py index 793e284f..2f34e485 100644 --- a/src/network/connectionpool.py +++ b/src/network/connectionpool.py @@ -40,8 +40,7 @@ class BMConnectionPool(object): if not i.fullyEstablished: continue try: - with i.objectsNewToMeLock: - del i.objectsNewToMe[hashid] + del i.objectsNewToMe[hashid] except KeyError: with i.objectsNewToThemLock: i.objectsNewToThem[hashid] = time.time() diff --git a/src/network/downloadthread.py b/src/network/downloadthread.py index 35616f1b..ec921c58 100644 --- a/src/network/downloadthread.py +++ b/src/network/downloadthread.py @@ -3,10 +3,8 @@ import threading import time import addresses -#from bmconfigparser import BMConfigParser from debug import logger from helper_threading import StoppableThread -#from inventory import Inventory from network.connectionpool import BMConnectionPool import protocol from state import missingObjects @@ -49,32 +47,15 @@ class DownloadThread(threading.Thread, StoppableThread): for i in connections: now = time.time() timedOut = now - DownloadThread.requestTimeout - # this may take a while, but it needs a consistency so I think it's better to lock a bigger chunk - with i.objectsNewToMeLock: - try: - downloadPending = len(list((k for k, v in i.objectsNewToMe.iteritems() if k in missingObjects and missingObjects[k] > timedOut and not v))) - except KeyError: - continue - if downloadPending >= DownloadThread.minPending: - continue - # keys with True values in the dict - try: - request = list((k for k, v in i.objectsNewToMe.iteritems() if k not in missingObjects or missingObjects[k] < timedOut)) - except KeyError: - continue - random.shuffle(request) - if len(request) > requestChunk - downloadPending: - request = request[:max(1, requestChunk - downloadPending)] - if not request: - continue - # mark them as pending - for k in request: - i.objectsNewToMe[k] = False - missingObjects[k] = now + try: + request = i.objectsNewToMe.randomKeys(requestChunk) + except KeyError: + continue payload = bytearray() payload.extend(addresses.encodeVarint(len(request))) for chunk in request: payload.extend(chunk) + missingObjects[k] = now i.append_write_buf(protocol.CreatePacket('getdata', payload)) logger.debug("%s:%i Requesting %i objects", i.destination.host, i.destination.port, len(request)) requested += len(request) diff --git a/src/network/objectracker.py b/src/network/objectracker.py index 62f01e4f..a786c696 100644 --- a/src/network/objectracker.py +++ b/src/network/objectracker.py @@ -5,6 +5,7 @@ from threading import RLock from debug import logger from inventory import Inventory from network.dandelion import Dandelion +from randomtrakcingdict import RandomTrackingDict from state import missingObjects haveBloom = False @@ -32,8 +33,7 @@ class ObjectTracker(object): initialTimeOffset = 60 def __init__(self): - self.objectsNewToMe = {} - self.objectsNewToMeLock = RLock() + self.objectsNewToMe = RandomTrackingDict() self.objectsNewToThem = {} self.objectsNewToThemLock = RLock() self.initInvBloom() @@ -61,9 +61,6 @@ class ObjectTracker(object): self.initAddrBloom() else: # release memory - with self.objectsNewToMeLock: - tmp = self.objectsNewToMe.copy() - self.objectsNewToMe = tmp deadline = time.time() - ObjectTracker.trackingExpires with self.objectsNewToThemLock: self.objectsNewToThem = {k: v for k, v in self.objectsNewToThem.iteritems() if v >= deadline} @@ -88,9 +85,8 @@ class ObjectTracker(object): if hashId in Dandelion().hashMap: Dandelion().fluffTrigger(hashId) if hashId not in missingObjects: - missingObjects[hashId] = time.time() - ObjectTracker.initialTimeOffset - with self.objectsNewToMeLock: - self.objectsNewToMe[hashId] = True + missingObjects[hashId] = True + self.objectsNewToMe[hashId] = True def hasAddr(self, addr): if haveBloom: diff --git a/src/randomtrackingdict.py b/src/randomtrackingdict.py new file mode 100644 index 00000000..466acc41 --- /dev/null +++ b/src/randomtrackingdict.py @@ -0,0 +1,118 @@ +import random +from threading import RLock +from time import time + +class RandomTrackingDict(object): + maxPending = 10 + pendingTimeout = 60 + def __init__(self): # O(1) + self.dictionary = {} + self.indexDict = [] + self.len = 0 + self.pendingLen = 0 + self.lastPoll = 0 + self.lock = RLock() + + def __len__(self): + return self.len + + def __contains__(self, key): + return key in self.dictionary + + def __getitem__(self, key): + return self.dictionary[key][1] + + def __setitem__(self, key, value): + with self.lock: + if key in self.dictionary: + self.dictionary[key][1] = value + else: + self.indexDict.append(key) + self.dictionary[key] = [self.len, value] + self.len += 1 + + def __delitem__(self, key): + if not key in self.dictionary: + raise KeyError + with self.lock: + index = self.dictionary[key][0] + self.indexDict[index] = self.indexDict[self.len - 1] + self.dictionary[self.indexDict[index]][0] = index + # if the following del is batched, performance of this single + # operation can improve 4x, but it's already very fast so we'll + # ignore it for the time being + del self.indexDict[-1] + del self.dictionary[key] + self.len -= 1 + if index >= self.len - self.pendingLen: + self.pendingLen -= 1 + + def setMaxPending(self, maxPending): + self.maxPending = maxPending + + def setPendingTimeout(self, pendingTimeout): + self.pendingTimeout = pendingTimeout + + def randomKeys(self, count=1): + if self.lastPoll + self.pendingTimeout < time(): + with self.lock: + self.pendingLen = 0 + if self.len == 0 or self.pendingLen >= self.maxPending: + raise KeyError + with self.lock: + available = self.len - self.pendingLen + if count > available: + count = available + retval = random.sample(self.indexDict[:self.len - self.pendingLen], count) + for i in retval[::-1]: + # swap with one below lowest pending + self.pendingLen += 1 + swapKey = self.indexDict[-self.pendingLen] + curIndex = self.dictionary[i][0] + self.indexDict[-self.pendingLen] = i + self.indexDict[curIndex] = swapKey + self.dictionary[i][0] = self.len - self.pendingLen + self.dictionary[swapKey][0] = curIndex + self.lastPoll = time() + return retval + +if __name__ == '__main__': + def randString(): + retval = b'' + for _ in range(32): + retval += chr(random.randint(0,255)) + return retval + + a = [] + k = RandomTrackingDict() + d = {} + +# print "populating normal dict" +# a.append(time()) +# for i in range(50000): +# d[randString()] = True +# a.append(time()) + print "populating random tracking dict" + a.append(time()) + for i in range(50000): + k[randString()] = True + a.append(time()) + print "done" + while len(k) > 0: + retval = k.randomKeys(1000) + if not retval: + print "error getting random keys" + #a.append(time()) + try: + k.randomKeys(100) + print "bad" + except KeyError: + pass + #a.append(time()) + for i in retval: + del k[i] + #a.append(time()) + a.append(time()) + + for x in range(len(a) - 1): + print "%i: %.3f" % (x, a[x+1] - a[x]) From 8d057424366b9ace958696f699995c5ab2633cd3 Mon Sep 17 00:00:00 2001 From: Peter Surda Date: Thu, 1 Feb 2018 12:20:41 +0100 Subject: [PATCH 361/407] Typo --- src/network/objectracker.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/network/objectracker.py b/src/network/objectracker.py index a786c696..044d11a2 100644 --- a/src/network/objectracker.py +++ b/src/network/objectracker.py @@ -5,7 +5,7 @@ from threading import RLock from debug import logger from inventory import Inventory from network.dandelion import Dandelion -from randomtrakcingdict import RandomTrackingDict +from randomtrackingdict import RandomTrackingDict from state import missingObjects haveBloom = False From c5dc7fc903146128a88af1e3edd36617b387b9f0 Mon Sep 17 00:00:00 2001 From: Peter Surda Date: Thu, 1 Feb 2018 12:26:54 +0100 Subject: [PATCH 362/407] Typo --- src/network/downloadthread.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/network/downloadthread.py b/src/network/downloadthread.py index ec921c58..d81b06e8 100644 --- a/src/network/downloadthread.py +++ b/src/network/downloadthread.py @@ -55,7 +55,7 @@ class DownloadThread(threading.Thread, StoppableThread): payload.extend(addresses.encodeVarint(len(request))) for chunk in request: payload.extend(chunk) - missingObjects[k] = now + missingObjects[chunk] = now i.append_write_buf(protocol.CreatePacket('getdata', payload)) logger.debug("%s:%i Requesting %i objects", i.destination.host, i.destination.port, len(request)) requested += len(request) From 290b87a49f9f64714862522227af04cf4b76bbb3 Mon Sep 17 00:00:00 2001 From: Peter Surda Date: Thu, 1 Feb 2018 12:48:14 +0100 Subject: [PATCH 363/407] Download fixes - don't expire too quickly - ignore connections that haven't been fully established yet --- src/network/downloadthread.py | 3 +-- src/network/objectracker.py | 2 +- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/src/network/downloadthread.py b/src/network/downloadthread.py index d81b06e8..f3beb77c 100644 --- a/src/network/downloadthread.py +++ b/src/network/downloadthread.py @@ -38,7 +38,7 @@ class DownloadThread(threading.Thread, StoppableThread): while not self._stopped: requested = 0 # Choose downloading peers randomly - connections = BMConnectionPool().inboundConnections.values() + BMConnectionPool().outboundConnections.values() + connections = [x for x in BMConnectionPool().inboundConnections.values() + BMConnectionPool().outboundConnections.values() if x.fullyEstablished] random.shuffle(connections) try: requestChunk = max(int(min(DownloadThread.maxRequestChunk, len(missingObjects)) / len(connections)), 1) @@ -46,7 +46,6 @@ class DownloadThread(threading.Thread, StoppableThread): requestChunk = 1 for i in connections: now = time.time() - timedOut = now - DownloadThread.requestTimeout try: request = i.objectsNewToMe.randomKeys(requestChunk) except KeyError: diff --git a/src/network/objectracker.py b/src/network/objectracker.py index 044d11a2..327304d9 100644 --- a/src/network/objectracker.py +++ b/src/network/objectracker.py @@ -85,7 +85,7 @@ class ObjectTracker(object): if hashId in Dandelion().hashMap: Dandelion().fluffTrigger(hashId) if hashId not in missingObjects: - missingObjects[hashId] = True + missingObjects[hashId] = time.time() self.objectsNewToMe[hashId] = True def hasAddr(self, addr): From 167d946435c67aeed5afa38e5dbb62bfa48d4a81 Mon Sep 17 00:00:00 2001 From: Peter Surda Date: Thu, 1 Feb 2018 14:43:14 +0100 Subject: [PATCH 364/407] RandomTrackingDict now tracks properly - it didn't put the correct keys at the end --- src/randomtrackingdict.py | 37 +++++++++++++++++++++++++------------ 1 file changed, 25 insertions(+), 12 deletions(-) diff --git a/src/randomtrackingdict.py b/src/randomtrackingdict.py index 466acc41..8c3fa6aa 100644 --- a/src/randomtrackingdict.py +++ b/src/randomtrackingdict.py @@ -31,21 +31,37 @@ class RandomTrackingDict(object): self.dictionary[key] = [self.len, value] self.len += 1 + def _swap(self, i1, i2): + with self.lock: + key1 = self.indexDict[i1] + key2 = self.indexDict[i2] + self.indexDict[i1] = key2 + self.indexDict[i2] = key1 + self.dictionary[key1][0] = i2 + self.dictionary[key2][0] = i1 + # for quick reassignment + return i2 + def __delitem__(self, key): if not key in self.dictionary: raise KeyError with self.lock: index = self.dictionary[key][0] - self.indexDict[index] = self.indexDict[self.len - 1] - self.dictionary[self.indexDict[index]][0] = index + # not pending + if index < self.len - self.pendingLen: + # left of pending part + index = self._swap(index, self.len - self.pendingLen - 1) + # pending + else: + self.pendingLen -= 1 + # end + self._swap(index, self.len - 1) # if the following del is batched, performance of this single # operation can improve 4x, but it's already very fast so we'll # ignore it for the time being del self.indexDict[-1] del self.dictionary[key] self.len -= 1 - if index >= self.len - self.pendingLen: - self.pendingLen -= 1 def setMaxPending(self, maxPending): self.maxPending = maxPending @@ -63,16 +79,13 @@ class RandomTrackingDict(object): available = self.len - self.pendingLen if count > available: count = available - retval = random.sample(self.indexDict[:self.len - self.pendingLen], count) - for i in retval[::-1]: + randomIndex = random.sample(range(self.len - self.pendingLen), count) + retval = [self.indexDict[i] for i in randomIndex] + + for i in sorted(randomIndex, reverse=True): # swap with one below lowest pending + self._swap(i, self.len - self.pendingLen - 1) self.pendingLen += 1 - swapKey = self.indexDict[-self.pendingLen] - curIndex = self.dictionary[i][0] - self.indexDict[-self.pendingLen] = i - self.indexDict[curIndex] = swapKey - self.dictionary[i][0] = self.len - self.pendingLen - self.dictionary[swapKey][0] = curIndex self.lastPoll = time() return retval From 2f150dcdd781c04cc119a553619e854f83746df5 Mon Sep 17 00:00:00 2001 From: PyBitmessage Translations Date: Thu, 1 Feb 2018 19:17:51 +0100 Subject: [PATCH 365/407] Auto-updated language eo from transifex --- src/translations/bitmessage_eo.qm | Bin 88527 -> 87188 bytes src/translations/bitmessage_eo.ts | 804 ++++++++++++++++-------------- 2 files changed, 434 insertions(+), 370 deletions(-) diff --git a/src/translations/bitmessage_eo.qm b/src/translations/bitmessage_eo.qm index 018a268ec83772d69447e5484473f13f7826df76..ea03b1b524bfce42b917b716e22c049b8ed25dfd 100644 GIT binary patch delta 5072 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 R6FSHLD8IGChO^bC{|PSl!65(u delta 5395 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 Uk=h3%npS-vx+RJpY_a2i0WC6As{jB1 diff --git a/src/translations/bitmessage_eo.ts b/src/translations/bitmessage_eo.ts index c662b0b5..beff4aca 100644 --- a/src/translations/bitmessage_eo.ts +++ b/src/translations/bitmessage_eo.ts @@ -2,17 +2,17 @@ AddAddressDialog - + Add new entry Aldoni novan elementon - + Label Etikedo - + Address Adreso @@ -20,70 +20,99 @@ EmailGatewayDialog - + Email gateway Retpoŝta kluzo - + Register on email gateway - Registri je retpoŝta kluzo + Registri ĉe retpoŝta kluzo - + Account status at email gateway Stato de retpoŝt-kluza konto - + Change account settings at email gateway Ŝanĝu agordojn de konto ĉe retpoŝta kluzo - + Unregister from email gateway Malregistri de retpoŝta kluzo - + Email gateway allows you to communicate with email users. Currently, only the Mailchuck email gateway (@mailchuck.com) is available. Retpoŝta kluzo ebligas al vi komunikadi kun retpoŝtaj uzantoj. Nuntempe, nur la retpoŝta kluzo de Mailchuck (@mailchuck.com) estas disponebla. - + Desired email address (including @mailchuck.com): Dezirata retpoŝta adreso (kune kun @mailchuck.com): + + + @mailchuck.com + @mailchuck.com + + + + Registration failed: + Registrado malsukcesis: + + + + The requested email address is not available, please try a new one. + La dezirata retpoŝtadreso ne estas disponebla, bonvolu provi alian. + + + + Sending email gateway registration request + Sendado de peto pri registrado ĉe retpoŝta kluzo + + + + Sending email gateway unregistration request + Sendado de peto pri malregistrado de retpoŝta kluzo + + + + Sending email gateway status request + Sendado de peto pri stato de retpoŝta kluzo + EmailGatewayRegistrationDialog - + Registration failed: - Registrado malsukcesis: + - + The requested email address is not available, please try a new one. Fill out the new desired email address (including @mailchuck.com) below: - La dezirata retpoŝtadreso ne estas disponebla, bonvolu provi alian. Entajpu novan deziratan adreson (kune kun @mailchuck.com) sube: + Email gateway registration - Registrado je retpoŝta kluzo + Email gateway allows you to communicate with email users. Currently, only the Mailchuck email gateway (@mailchuck.com) is available. Please type the desired email address (including @mailchuck.com) below: - Retpoŝta kluzo ebligas al vi komunikadi kun retpoŝtaj uzantoj. Nuntempe, nur la retpoŝta kluzo de Mailchuck (@mailchuck.com) estas disponebla. -Bonvolu entajpi deziratan retpoŝtadreson (kune kun @mailchuck.com) sube: + Mailchuck - + # You can use this to configure your email gateway account # Uncomment the setting you want to use # Here are the options: @@ -166,52 +195,52 @@ Bonvolu entajpi deziratan retpoŝtadreson (kune kun @mailchuck.com) sube: MainWindow - + Reply to sender Respondi al sendinto - + Reply to channel Respondi al kanalo - + Add sender to your Address Book Aldoni sendinton al via adresaro - + Add sender to your Blacklist Aldoni sendinton al via nigra listo - + Move to Trash Movi al rubujo - + Undelete - Malforviŝi + Malforigi - + View HTML code as formatted text Montri HTML-n kiel aranĝitan tekston - + Save message as... Konservi mesaĝon kiel… - + Mark Unread Marki kiel nelegitan - + New Nova @@ -236,12 +265,12 @@ Bonvolu entajpi deziratan retpoŝtadreson (kune kun @mailchuck.com) sube:Kopii adreson al tondejo - + Special address behavior... Speciala sinteno de adreso… - + Email gateway Retpoŝta kluzo @@ -251,37 +280,37 @@ Bonvolu entajpi deziratan retpoŝtadreson (kune kun @mailchuck.com) sube:Forigi - + Send message to this address Sendi mesaĝon al tiu adreso - + Subscribe to this address Aboni tiun adreson - + Add New Address Aldoni novan adreson - + Copy destination address to clipboard Kopii cel-adreson al tondejo - + Force send Devigi sendadon - + One of your addresses, %1, is an old version 1 address. Version 1 addresses are no longer supported. May we delete it now? Iu de viaj adresoj, %1, estas malnova versio 1 adreso. Versioj 1 adresoj ne estas jam subtenataj. Ĉu ni povas forigi ĝin? - + Waiting for their encryption key. Will request it again soon. Atendado je ilia ĉifroŝlosilo. Baldaŭ petos ĝin denove. @@ -291,17 +320,17 @@ Bonvolu entajpi deziratan retpoŝtadreson (kune kun @mailchuck.com) sube: - + Queued. En atendovico. - + Message sent. Waiting for acknowledgement. Sent at %1 Mesaĝo sendita. Atendado je konfirmo. Sendita je %1 - + Message sent. Sent at %1 Mesaĝo sendita. Sendita je %1 @@ -311,77 +340,77 @@ Bonvolu entajpi deziratan retpoŝtadreson (kune kun @mailchuck.com) sube: - + Acknowledgement of the message received %1 Ricevis konfirmon de la mesaĝo je %1 - + Broadcast queued. Elsendo en atendovico. - + Broadcast on %1 Elsendo je %1 - + Problem: The work demanded by the recipient is more difficult than you are willing to do. %1 Problemo: la demandita laboro de la ricevonto estas pli malfacila ol vi pretas fari. %1 - + Problem: The recipient's encryption key is no good. Could not encrypt message. %1 Problemo: la ĉifroŝlosilo de la ricevonto estas rompita. Ne povis ĉifri la mesaĝon. %1 - + Forced difficulty override. Send should start soon. Devigita superado de limito de malfacilaĵo. Sendado devus baldaŭ komenci. - + Unknown status: %1 %2 Nekonata stato: %1 %2 - + Not Connected Ne konektita - + Show Bitmessage Montri Bitmesaĝon - + Send Sendi - + Subscribe Aboni - + Channel Kanalo - + Quit Eliri - + You may manage your keys by editing the keys.dat file stored in the same directory as this program. It is important that you back up this file. Vi povas administri viajn ŝlosilojn per redakti la dosieron keys.dat en la sama dosierujo kiel tiu programo. Estas grava, ke vi faru sekurkopion de tiu dosiero. - + You may manage your keys by editing the keys.dat file stored in %1 It is important that you back up this file. @@ -390,17 +419,17 @@ It is important that you back up this file. Estas grava, ke vi faru sekurkopion de tiu dosiero. - + Open keys.dat? Ĉu malfermi keys.dat? - + You may manage your keys by editing the keys.dat file stored in the same directory as this program. It is important that you back up this file. Would you like to open the file now? (Be sure to close Bitmessage before making any changes.) Vi povas administri viajn ŝlosilojn per redakti la dosieron keys.dat en la sama dosierujo kiel tiu programo. Estas grava ke vi faru sekurkopion de tiu dosiero. Ĉu vi volas malfermi la dosieron nun? (Bonvolu certigi ke Bitmesaĝo estas fermita antaŭ fari ŝanĝojn.) - + You may manage your keys by editing the keys.dat file stored in %1 It is important that you back up this file. Would you like to open the file now? (Be sure to close Bitmessage before making any changes.) @@ -409,37 +438,37 @@ It is important that you back up this file. Would you like to open the file now? Estas grava, ke vi faru sekurkopion de tiu dosiero. Ĉu vi volas malfermi la dosieron nun? (Bonvolu certigi ke Bitmesaĝo estas fermita antaŭ fari ŝanĝojn.) - + Delete trash? Ĉu malplenigi rubujon? - + Are you sure you want to delete all trashed messages? Ĉu vi certe volas forviŝi ĉiujn mesaĝojn el la rubujo? - + bad passphrase malprava pasvorto - + You must type your passphrase. If you don't have one then this is not the form for you. Vi devas tajpi vian pasvorton. Se vi ne havas pasvorton, tiu ĉi ne estas la prava formularo por vi. - + Bad address version number Erara numero de adresversio - + Your address version number must be a number: either 3 or 4. Via numero de adresversio devas esti: aŭ 3 aŭ 4. - + Your address version number must be either 3 or 4. Via numero de adresversio devas esti: aŭ 3 aŭ 4. @@ -509,22 +538,22 @@ Estas grava, ke vi faru sekurkopion de tiu dosiero. Ĉu vi volas malfermi la dos - + Connection lost Perdis konekton - + Connected Konektita - + Message trashed Movis mesaĝon al rubujo - + The TTL, or Time-To-Live is the length of time that the network will hold the message. The recipient must get it during this time. If your Bitmessage client does not hear an acknowledgement, it will resend the message automatically. The longer the Time-To-Live, the @@ -532,17 +561,17 @@ Estas grava, ke vi faru sekurkopion de tiu dosiero. Ĉu vi volas malfermi la dos La vivdaŭro signifas ĝis kiam la reto tenos la mesaĝon. La ricevonto devos elŝuti ĝin dum tiu tempo. Se via bitmesaĝa kliento ne ricevos konfirmon, ĝi resendos mesaĝon aŭtomate. Ju pli longa vivdaŭro, des pli laboron via komputilo bezonos fari por sendi mesaĝon. Vivdaŭro proksimume kvin aŭ kvar horoj estas ofte konvena. - + Message too long Mesaĝo tro longa - + The message that you are trying to send is too long by %1 bytes. (The maximum is 261644 bytes). Please cut it down before sending. La mesaĝon kiun vi provis sendi estas tro longa je %1 bitokoj. (La maksimumo estas 261644 bitokoj.) Bonvolu mallongigi ĝin antaŭ sendado. - + Error: Your account wasn't registered at an email gateway. Sending registration now as %1, please wait for the registration to be processed before retrying sending. Eraro: Via konto ne estas registrita je retpoŝta kluzo. Registranta nun kiel %1, bonvolu atendi ĝis la registrado finos antaŭ vi reprovos sendi iun ajn. @@ -587,57 +616,57 @@ Estas grava, ke vi faru sekurkopion de tiu dosiero. Ĉu vi volas malfermi la dos - + Error: You must specify a From address. If you don't have one, go to the 'Your Identities' tab. Eraro: Vi devas elekti sendontan adreson. Se vi ne havas iun, iru al langeto 'Viaj identigoj'. - + Address version number Numero de adresversio - + Concerning the address %1, Bitmessage cannot understand address version numbers of %2. Perhaps upgrade Bitmessage to the latest version. Dum prilaborado de adreso adreso %1, Bitmesaĝo ne povas kompreni numerojn %2 de adresversioj. Eble ĝisdatigu Bitmesaĝon al la plej nova versio. - + Stream number Fluo numero - + Concerning the address %1, Bitmessage cannot handle stream numbers of %2. Perhaps upgrade Bitmessage to the latest version. Dum prilaborado de adreso %1, Bitmesaĝo ne povas priservi %2 fluojn numerojn. Eble ĝisdatigu Bitmesaĝon al la plej nova versio. - + Warning: You are currently not connected. Bitmessage will do the work necessary to send the message but it won't send until you connect. Atentu: Vi ne estas nun konektita. Bitmesaĝo faros necesan laboron por sendi mesaĝon, tamen ĝi ne sendos ĝin antaŭ vi konektos. - + Message queued. Mesaĝo envicigita. - + Your 'To' field is empty. Via "Ricevonto"-kampo malplenas. - + Right click one or more entries in your address book and select 'Send message to this address'. Dekstre alklaku kelka(j)n elemento(j)n en via adresaro kaj elektu 'Sendi mesaĝon al tiu adreso'. - + Fetched address from namecoin identity. Venigis adreson de namecoin-a identigo. - + New Message Nova mesaĝo @@ -647,9 +676,9 @@ Estas grava, ke vi faru sekurkopion de tiu dosiero. Ĉu vi volas malfermi la dos - + Sending email gateway registration request - Sendado de peto pri registrado je retpoŝta kluzo + @@ -662,430 +691,430 @@ Estas grava, ke vi faru sekurkopion de tiu dosiero. Ĉu vi volas malfermi la dos La adreso kiun vi enmetis estas malĝusta. Ignoras ĝin. - + Error: You cannot add the same address to your address book twice. Try renaming the existing one if you want. Eraro: Vi ne povas duoble aldoni la saman adreson al via adresaro. Provu renomi la ekzistan se vi volas. - + Error: You cannot add the same address to your subscriptions twice. Perhaps rename the existing one if you want. Eraro: Vi ne povas aldoni duoble la saman adreson al viaj abonoj. Eble renomi la ekzistan se vi volas. - + Restart Restartigi - + You must restart Bitmessage for the port number change to take effect. Vi devas restartigi Bitmesaĝon por ke la ŝanĝo de la numero de pordo (Port Number) efektivigu. - + Bitmessage will use your proxy from now on but you may want to manually restart Bitmessage now to close existing connections (if any). - Bitmesaĝo uzos vian prokurilon (proxy) ekde nun, sed eble vi volas permane restartigi Bitmesaĝon nun, por ke ĝi fermu eblajn ekzistajn konektojn. + Bitmesaĝo uzos retperanton (proxy) ekde nun, sed eble vi volas permane restartigi Bitmesaĝon nun, por ke ĝi fermu eblajn ekzistajn konektojn. - + Number needed Numero bezonata - + Your maximum download and upload rate must be numbers. Ignoring what you typed. Maksimumaj elŝutrapido kaj alŝutrapido devas esti numeroj. Ignoras kion vi enmetis. - + Will not resend ever Resendos neniam - + Note that the time limit you entered is less than the amount of time Bitmessage waits for the first resend attempt therefore your messages will never be resent. Rigardu, ke la templimon vi enmetis estas pli malgrandan ol tempo dum kiu Bitmesaĝo atendas por resendi unuafoje, do viaj mesaĝoj estos senditaj neniam. - + Sending email gateway unregistration request - Sendado de peto pri malregistrado de retpoŝta kluzo + - + Sending email gateway status request - Sendado de peto pri stato de retpoŝta kluzo + - + Passphrase mismatch Pasfrazoj malsamas - + The passphrase you entered twice doesn't match. Try again. - La pasfrazo kiun vi duoble enmetis malsamas. Provu denove. + Entajpitaj pasfrazoj malsamas. Provu denove. - + Choose a passphrase Elektu pasfrazon - + You really do need a passphrase. Vi ja vere bezonas pasfrazon. - + Address is gone Adreso foriris - + Bitmessage cannot find your address %1. Perhaps you removed it? Bitmesaĝo ne povas trovi vian adreson %1. Ĉu eble vi forviŝis ĝin? - + Address disabled Adreso malŝaltita - + Error: The address from which you are trying to send is disabled. You'll have to enable it on the 'Your Identities' tab before using it. Eraro: La adreso kun kiu vi provas sendi estas malŝaltita. Vi devos ĝin ŝalti en la langeto 'Viaj identigoj' antaŭ uzi ĝin. - + Entry added to the Address Book. Edit the label to your liking. - Aldonis elementon al adresaro. Redaktu la etikedon laŭvole. + - + Entry added to the blacklist. Edit the label to your liking. Aldonis elementon al la nigra listo. Redaktu la etikedon laŭvole. - + Error: You cannot add the same address to your blacklist twice. Try renaming the existing one if you want. Eraro: Vi ne povas duoble aldoni la saman adreson al via nigra listo. Provu renomi la jaman se vi volas. - + Moved items to trash. Movis elementojn al rubujo. - + Undeleted item. - Malforviŝis elementon. + Malforigis elementon. - + Save As... Konservi kiel… - + Write error. Skriberaro. - + No addresses selected. Neniu adreso elektita. - + If you delete the subscription, messages that you already received will become inaccessible. Maybe you can consider disabling the subscription instead. Disabled subscriptions will not receive new messages, but you can still view messages you already received. Are you sure you want to delete the subscription? - Se vi forviŝos abonon, mesaĝojn kiujn vi jam ricevis, igos neatingeblajn. Eble vi devus anstataŭ malaktivigi abonon. Malaktivaj abonoj ne ricevos novajn mesaĝojn, tamen vi plu povos legi mesaĝojn kiujn ci jam ricevis. + Se vi forigos abonon, mesaĝojn kiujn vi jam ricevis, igos neatingeblajn. Eble vi devus anstataŭ malaktivigi abonon. Malaktivaj abonoj ne ricevos novajn mesaĝojn, tamen vi plu povos legi mesaĝojn kiujn ci jam ricevis. -Ĉu vi certe volas forviŝi la abonon? +Ĉu vi certe volas forigi la abonon? - + If you delete the channel, messages that you already received will become inaccessible. Maybe you can consider disabling the channel instead. Disabled channels will not receive new messages, but you can still view messages you already received. Are you sure you want to delete the channel? - Se vi forviŝos kanalon, mesaĝojn kiujn vi jam ricevis, igos neatingeblajn. Eble vi devus anstataŭ malaktivigi kanalon. Malaktivaj kanaloj ne ricevos novajn mesaĝojn, tamen vi plu povos legi mesaĝojn kiujn ci jam ricevis. + Se vi forigos kanalon, mesaĝojn kiujn vi jam ricevis, igos neatingeblajn. Eble vi devus anstataŭ malaktivigi kanalon. Malaktivaj kanaloj ne ricevos novajn mesaĝojn, tamen vi plu povos legi mesaĝojn kiujn ci jam ricevis. -Ĉu vi certe volas forviŝi la kanalon? +Ĉu vi certe volas forigi la kanalon? - + Do you really want to remove this avatar? Ĉu vi certe volas forviŝi tiun ĉi avataron? - + You have already set an avatar for this address. Do you really want to overwrite it? Vi jam agordis avataron por tiu ĉi adreso. Ĉu vi vere volas superskribi ĝin? - + Start-on-login not yet supported on your OS. Starto-dum-ensaluto ne estas ankoraŭ ebla en via operaciumo. - + Minimize-to-tray not yet supported on your OS. Plejetigo al taskopleto ne estas ankoraŭ ebla en via operaciumo. - + Tray notifications not yet supported on your OS. Taskopletaj sciigoj ne estas ankoraŭ eblaj en via operaciumo. - + Testing... Testado… - + This is a chan address. You cannot use it as a pseudo-mailing list. - Tio ĉi estas kanaladreso. Vi ne povas ĝin uzi kiel kvazaŭ-dissendolisto. + - + The address should start with ''BM-'' - La adreso komencu kun "BM-" + La adreso komencu kun “BM-” - + The address is not typed or copied correctly (the checksum failed). - La adreso ne estis prave tajpita aŭ kopiita (kontrolsumo malsukcesis). + La adreso ne estis ĝuste tajpita aŭ kopiita (kontrolsumo malsukcesis). - + The version number of this address is higher than this software can support. Please upgrade Bitmessage. - La numero de adresversio estas pli alta ol tiun, kiun la programo poveblas subteni. Bonvolu ĝisdatigi Bitmesaĝon. + La numero de adresversio estas pli alta ol tiu, kiun la programo poveblas subteni. Bonvolu ĝisdatigi Bitmesaĝon. - + The address contains invalid characters. La adreso enhavas malpermesitajn simbolojn. - + Some data encoded in the address is too short. - Kelkaj datumoj koditaj en la adreso estas tro mallongaj. + Iuj datumoj koditaj en la adreso estas tro mallongaj. + + + + Some data encoded in the address is too long. + Iuj datumoj koditaj en la adreso estas tro longaj. + + + + Some data encoded in the address is malformed. + Iuj datumoj koditaj en la adreso estas misformitaj. - Some data encoded in the address is too long. - Kelkaj datumoj koditaj en la adreso estas tro longaj. - - - - Some data encoded in the address is malformed. - Kelkaj datumoj koditaj en la adreso estas misformitaj. - - - Enter an address above. - Enmetu adreson supre. + - + Address is an old type. We cannot display its past broadcasts. - Malnova speco de adreso. Ne povas montri ĝiajn antaŭajn elsendojn. + Malnova tipo de adreso. Ne povas montri ĝiajn antaŭajn elsendojn. - + There are no recent broadcasts from this address to display. Neniaj lastatempaj elsendoj de tiu ĉi adreso por montri. - + You are using TCP port %1. (This can be changed in the settings). - Vi uzas TCP-pordon %1 (tio ĉi estas ŝanĝebla en la agordoj). + - + Bitmessage Bitmesaĝo - + Identities Identigoj - + New Identity Nova identigo - + Search Serĉi - + All Ĉio - + To Al - + From De - + Subject Temo - + Message Mesaĝo - + Received Ricevita je - + Messages Mesaĝoj - + Address book Etikedo - + Address Adreso - + Add Contact Aldoni kontakton - + Fetch Namecoin ID Venigi Namecoin ID - + Subject: Temo: - + From: De: - + To: Al: - + Send ordinary Message Sendi ordinaran mesaĝon - + Send Message to your Subscribers Sendi mesaĝon al viaj abonantoj - + TTL: Vivdaŭro: - + Subscriptions Abonoj - + Add new Subscription Aldoni novan abonon - + Chans Kanaloj - + Add Chan Aldoni kanalon - + File Dosiero - + Settings Agordoj - + Help Helpo - + Import keys Enporti ŝlosilojn - + Manage keys Administri ŝlosilojn - + Ctrl+Q Stir+Q - + F1 F1 - + Contact support Peti pri helpo - + About Pri - + Regenerate deterministic addresses Regeneri antaŭkalkuleblan adreson - + Delete all trashed messages Forviŝi ĉiujn mesaĝojn el rubujo - + Join / Create chan Aniĝi / Krei kanalon @@ -1110,67 +1139,67 @@ Are you sure you want to delete the channel? Aldoni novan elementon - + Display the %1 recent broadcast(s) from this address. - Montri la %1 lasta(j)n elsendo(j)n de tiu adreso. + Montri %1 lasta(j)n elsendo(j)n de tiu ĉi adreso. - + New version of PyBitmessage is available: %1. Download it from https://github.com/Bitmessage/PyBitmessage/releases/latest La nova versio de PyBitmessage estas disponebla: %1. Elŝutu ĝin de https://github.com/Bitmessage/PyBitmessage/releases/latest - + Waiting for PoW to finish... %1% Atendado ĝis laborpruvo finiĝos… %1% - + Shutting down Pybitmessage... %1% Fermado de PyBitmessage… %1% - + Waiting for objects to be sent... %1% Atendado ĝis objektoj estos senditaj… %1% - + Saving settings... %1% Konservado de agordoj… %1% - + Shutting down core... %1% Fermado de kerno… %1% - + Stopping notifications... %1% Haltigado de sciigoj… %1% - + Shutdown imminent... %1% Fermado tuj… %1% - + %n hour(s) %n horo%n horoj - + %n day(s) %n tago%n tagoj - + Shutting down PyBitmessage... %1% Fermado de PyBitmessage… %1% - + Sent Senditaj @@ -1215,86 +1244,86 @@ Are you sure you want to delete the channel? Atentu: Via disko aŭ subdisko estas plenplena. Bitmesaĝo fermiĝos. - + Error! Could not find sender address (your address) in the keys.dat file. Eraro! Ne povas trovi adreson de sendanto (vian adreson) en la dosiero keys.dat. - + Doing work necessary to send broadcast... Kalkulado de laborpruvo, kiu endas por sendi elsendon… - + Broadcast sent on %1 Elsendo sendita je %1 - + Encryption key was requested earlier. Peto pri ĉifroŝlosilo jam sendita. - + Sending a request for the recipient's encryption key. Sendado de peto pri ĉifroŝlosilo de ricevonto. - + Looking up the receiver's public key Serĉado de publika ĉifroŝlosilo de ricevonto - + Problem: Destination is a mobile device who requests that the destination be included in the message but this is disallowed in your settings. %1 Eraro: celadreso estas portebla aparato kiu necesas, ke la celadreso estu enhavita en la mesaĝo, sed tio estas malpermesita ne viaj agordoj. %1 - + Doing work necessary to send message. There is no required difficulty for version 2 addresses like this. Kalkulado de laborpruvo, kiu endas por sendi mesaĝon. Malfacilaĵo ne estas bezonata por adresoj versioj 2, kiel tiu ĉi adreso. - + Doing work necessary to send message. Receiver's required difficulty: %1 and %2 Kalkulado de laborpruvo, kiu endas por sendi mesaĝon. Ricevonto postulas malfacilaĵon: %1 kaj %2 - + Problem: The work demanded by the recipient (%1 and %2) is more difficult than you are willing to do. %3 Eraro: la demandita laboro de la ricevonto (%1 kaj %2) estas pli malfacila ol vi pretas fari. %3 - + Problem: You are trying to send a message to yourself or a chan but your encryption key could not be found in the keys.dat file. Could not encrypt message. %1 Eraro: Vi provis sendi mesaĝon al vi mem aŭ al kanalo, tamen via ĉifroŝlosilo ne estas trovebla en la dosiero keys.dat. Mesaĝo ne povis esti ĉifrita. %1 - + Doing work necessary to send message. Kalkulado de laborpruvo, kiu endas por sendi mesaĝon. - + Message sent. Waiting for acknowledgement. Sent on %1 Mesaĝo sendita. Atendado je konfirmo. Sendita je %1 - + Doing work necessary to request encryption key. Kalkulado de laborpruvo, kiu endas por peti pri ĉifroŝlosilo. - + Broadcasting the public key request. This program will auto-retry if they are offline. Elsendado de peto pri publika ĉifroŝlosilo. La programo reprovos se ili estas eksterrete. - + Sending public key request. Waiting for reply. Requested at %1 Sendado de peto pri publika ĉifroŝlosilo. Atendado je respondo. Petis je %1 @@ -1309,44 +1338,44 @@ Ricevonto postulas malfacilaĵon: %1 kaj %2 UPnP pord-mapigo forigita - + Mark all messages as read Marki ĉiujn mesaĝojn kiel legitajn - + Are you sure you would like to mark all messages read? Ĉu vi certe volas marki ĉiujn mesaĝojn kiel legitajn? - + Doing work necessary to send broadcast. Kalkulado de laborpruvo, kiu endas por sendi elsendon. - + Proof of work pending Laborpruvo haltigita - + %n object(s) pending proof of work Haltigis laborpruvon por %n objektoHaltigis laborpruvon por %n objektoj - + %n object(s) waiting to be distributed %n objekto atendas je sendato%n objektoj atendas je sendato - + Wait until these tasks finish? Ĉu atendi ĝis tiujn taskojn finos? Problem communicating with proxy: %1. Please check your network settings. - Eraro dum komunikado kun prokurilo: %1. Bonvolu kontroli viajn retajn agordojn. + Eraro dum komunikado kun retperanto: %1. Bonvolu kontroli viajn retajn agordojn. @@ -1404,12 +1433,12 @@ Ricevonto postulas malfacilaĵon: %1 kaj %2 Via(j) vidprocesoro(j) ne kalkulis senerare, malaktiviganta OpenCL. Bonvolu raporti tion al programistoj. - + Set notification sound... Agordi sciigan sonon… - + Welcome to easy and secure Bitmessage * send messages to other people @@ -1423,120 +1452,140 @@ Bonvenon al facila kaj sekura Bitmesaĝo * babili kun aliaj uloj en mesaĝ-kanaloj - + not recommended for chans malkonsilinda por kanaloj - + + Quiet Mode + Silenta reĝimo + + + Problems connecting? Try enabling UPnP in the Network Settings Ĉu problemo kun konektado? Provu aktivigi UPnP en retaj agordoj. - + You are trying to send an email instead of a bitmessage. This requires registering with a gateway. Attempt to register? Vi provas sendi retmesaĝon anstataŭ bitmesaĝ-mesaĝon. Tio ĉi postulas registri ĉe retpoŝta kluzo. Ĉu provi registri? - + Error: Bitmessage addresses start with BM- Please check the recipient address %1 Eraro: bitmesaĝaj adresoj komenciĝas kun BM-. Bonvolu kontroli la adreson de ricevonto %1 - + Error: The recipient address %1 is not typed or copied correctly. Please check it. Eraro: la adreso de ricevonto %1 estas malprave tajpita aŭ kopiita. Bonvolu kontroli ĝin. - + Error: The recipient address %1 contains invalid characters. Please check it. Eraro: la adreso de ricevonto %1 enhavas malpermesatajn simbolojn. Bonvolu kontroli ĝin. - + Error: The version of the recipient address %1 is too high. Either you need to upgrade your Bitmessage software or your acquaintance is being clever. Eraro: la versio de adreso de ricevonto %1 estas tro alta. Eble vi devas ĝisdatigi vian bitmesaĝan programon aŭ via sagaca konato uzas alian programon. - + Error: Some data encoded in the recipient address %1 is too short. There might be something wrong with the software of your acquaintance. Eraro: kelkaj datumoj koditaj en la adreso de ricevonto %1 estas tro mallongaj. Povus esti ke io en la programo de via konato malfunkcias. - + Error: Some data encoded in the recipient address %1 is too long. There might be something wrong with the software of your acquaintance. Eraro: kelkaj datumoj koditaj en la adreso de ricevonto %1 estas tro longaj. Povus esti ke io en la programo de via konato malfunkcias. - + Error: Some data encoded in the recipient address %1 is malformed. There might be something wrong with the software of your acquaintance. Eraro: kelkaj datumoj koditaj en la adreso de ricevonto %1 estas misformitaj. Povus esti ke io en la programo de via konato malfunkcias. - + Error: Something is wrong with the recipient address %1. Eraro: io malĝustas kun la adreso de ricevonto %1. - + + Error: %1 + Eraro: %1 + + + From %1 De %1 - + Synchronisation pending Samtempigado haltigita - + Bitmessage hasn't synchronised with the network, %n object(s) to be downloaded. If you quit now, it may cause delivery delays. Wait until the synchronisation finishes? Bitmesaĝo ne estas samtempigita kun la reto, %n objekto elŝutendas. Se vi eliros nun, tio povas igi malfruiĝojn de liveradoj. Ĉu atendi ĝis la samtempigado finiĝos?Bitmesaĝo ne estas samtempigita kun la reto, %n objektoj elŝutendas. Se vi eliros nun, tio povas igi malfruiĝojn de liveradoj. Ĉu atendi ĝis la samtempigado finiĝos? - + Not connected Nekonektita - + Bitmessage isn't connected to the network. If you quit now, it may cause delivery delays. Wait until connected and the synchronisation finishes? Bitmesaĝo ne estas konektita al la reto. Se vi eliros nun, tio povas igi malfruiĝojn de liveradoj. Ĉu atendi ĝis ĝi konektos kaj la samtempigado finiĝos? - + Waiting for network connection... Atendado je retkonekto… - + Waiting for finishing synchronisation... Atendado ĝis samtempigado finiĝos… - + You have already set a notification sound for this address book entry. Do you really want to overwrite it? Vi jam agordis sciigan sonon por tiu ĉi adreso. Ĉu vi volas anstataŭigi ĝin? + + + Go online + Konekti + + + + Go offline + Malkonekti + MessageView - + Follow external link Sekvi la eksteran ligilon - + The link "%1" will open in a browser. It may be a security risk, it could de-anonymise you or download malicious data. Are you sure? La ligilo "%1" estos malfermita per foliumilo. Tio povas esti malsekura, ĝi povos malanonimigi vin aŭ elŝuti malicajn datumojn. Ĉu vi certas? - + HTML detected, click here to display HTML detektita, alklaku ĉi tie por montri - + Click here to disable HTML Alklaku ĉi tie por malaktivigi HTML @@ -1559,99 +1608,99 @@ Eble vi devas ĝisdatigi Bitmesaĝon. NewAddressDialog - + Create new Address Krei novan adreson - + Here you may generate as many addresses as you like. Indeed, creating and abandoning addresses is encouraged. You may generate addresses by using either random numbers or by using a passphrase. If you use a passphrase, the address is called a "deterministic" address. The 'Random Number' option is selected by default but deterministic addresses have several pros and cons: - Tie ĉi vi povas generi tiom da adresoj, kiom vi volas. Ververe kreado kaj forlasado de adresoj estas konsilinda. Vi povas krei adresojn uzante hazardajn nombrojn aŭ pasfrazon. Se vi uzos pasfrazon, la adreso estas nomita kiel ‘antaŭkalkulebla’ (determinisma) adreso. -La ‘hazardnombra’ adreso estas antaŭagordita, sed antaŭkalkuleblaj adresoj havas kelkajn bonaĵojn kaj malbonaĵojn: + Tie ĉi vi povas generi tiom da adresoj, kiom vi volas. Ververe krei kaj forlasi adresojn estas konsilinda. Vi povas krei adresojn uzante hazardajn nombrojn aŭ pasfrazon. Se vi uzos pasfrazon, la adreso estas nomita kiel “antaŭkalkulebla” (determinisma) adreso. +La “hazardnombra” adreso estas antaŭagordita, sed antaŭkalkuleblaj adresoj havas kelkajn bonaĵojn kaj malbonaĵojn: - + <html><head/><body><p><span style=" font-weight:600;">Pros:<br/></span>You can recreate your addresses on any computer from memory. <br/>You need-not worry about backing up your keys.dat file as long as you can remember your passphrase. <br/><span style=" font-weight:600;">Cons:<br/></span>You must remember (or write down) your passphrase if you expect to be able to recreate your keys if they are lost. <br/>You must remember the address version number and the stream number along with your passphrase. <br/>If you choose a weak passphrase and someone on the Internet can brute-force it, they can read your messages and send messages as you.</p></body></html> - <html><head/><body><p><span style=" font-weight:600;">Bonaĵoj:<br/></span>Vi poveblas rekrei viajn adresojn per iu ajn komputilo elkape.<br/>Vi ne devas klopodi fari sekurkopion de keys.dat dosiero tiel longe, dum vi memoras vian pasfrazon.<br/><span style=" font-weight:600;">Malbonaĵoj:<br/></span>Vi devas memori (aŭ konservi) vian pasfrazon se vi volas rekrei viajn ŝlosilojn kiam vi perdos ilin.<br/>Vi devas memori nombron de adresversio kaj de fluo kune kun vian pasfrazon.<br/>Se vi elektos malfortan pasfrazon kaj iu ajn Interrete eblos brutforti ĝin, li povos legi ĉiujn viajn mesaĝojn kaj sendi mesaĝojn kiel vi.</p></body></html> + <html><head/><body><p><span style=" font-weight:600;">Bonaĵoj:<br/></span>Vi poveblas rekrei viajn adresojn per iu ajn komputilo elkape.<br/>Vi ne devas klopodi fari sekurkopion de la dosiero keys.dat tiel longe, dum vi memoras vian pasfrazon.<br/><span style=" font-weight:600;">Malbonaĵoj:<br/></span>Vi devas memori (aŭ konservi) vian pasfrazon se vi volas rekrei viajn ŝlosilojn kiam vi perdos ilin.<br/>Vi devas memori nombron de adresversio kaj de fluo kune kun vian pasfrazon.<br/>Se vi elektos malfortan pasfrazon kaj iu ajn Interrete eblos brutforti ĝin, li povos legi ĉiujn viajn mesaĝojn kaj sendi mesaĝojn kiel vi.</p></body></html> - + Use a random number generator to make an address Uzi hazardnombran generilon por krei adreson - + Use a passphrase to make addresses Uzi pasfrazon por krei adreson - + Spend several minutes of extra computing time to make the address(es) 1 or 2 characters shorter - Pasigi kelkajn minutojn aldone kompute por fari la adreso(j)n 1 aŭ 2 signoj pli mallongaj + Pasigi kelkajn minutojn aldone kompute por krei adreso(j)n mallongaj je 1 aŭ 2 signoj - + Make deterministic addresses Fari antaŭkalkuleblan adreson - + Address version number: 4 Numero de adresversio: 4 - + In addition to your passphrase, you must remember these numbers: Kune kun vian pasfrazon vi devas memori jenajn numerojn: - + Passphrase Pasfrazo - + Number of addresses to make based on your passphrase: Nombro da farotaj adresoj bazante sur via pasfrazo: - + Stream number: 1 - Fluo numero: 1 + Numero de fluo: 1 - + Retype passphrase - Reenmeti pasfrazon + Pasfrazo ree - + Randomly generate address Hazardnombra adreso - + Label (not shown to anyone except you) Etikedo (ne montrata al iu ajn escepte de vi) - + Use the most available stream Uzi la plej disponeblan fluon - + (best if this is the first of many addresses you will create) - (plej bone se tiun ĉi estas la unuan de ĉiuj adresojn vi kreos) + (plej bone se ĝi estas la unua de ĉiuj adresojn vi kreos) - + Use the same stream as an existing address - Uzi saman fluon kiel ekzistan adreson + Uzi la saman fluon kiel ekzistan adreson - + (saves you some bandwidth and processing power) (konservas iomete da ret-trafiko kaj komput-povo) @@ -1659,78 +1708,83 @@ La ‘hazardnombra’ adreso estas antaŭagordita, sed antaŭkalkuleblaj adresoj NewSubscriptionDialog - + Add new entry Aldoni novan elementon - + Label Etikedo - + Address Adreso - + Enter an address above. - Enmetu adreson supre. + Entajpu adreson supre. SpecialAddressBehaviorDialog - + Special Address Behavior Speciala sinteno de adreso - + Behave as a normal address Sintenadi kiel normala adreso - + Behave as a pseudo-mailing-list address Sintenadi kiel kvazaŭ-elsendlista adreso - + Mail received to a pseudo-mailing-list address will be automatically broadcast to subscribers (and thus will be public). Mesaĝoj alvenintaj al kvazaŭ-dissendlisto estos aŭtomate dissenditaj al abonantoj (do ili estos publikaj). - + Name of the pseudo-mailing-list: - Nomo de la kvazaŭ-dissendlisto: + Nomo de kvazaŭ-dissendlisto: + + + + This is a chan address. You cannot use it as a pseudo-mailing list. + Tio ĉi estas kanaladreso. Vi ne povas ĝin uzi kiel kvazaŭ-dissendolisto. aboutDialog - + About Pri - + PyBitmessage - PyBitmessage + - + version ? - versio ? + - + <html><head/><body><p>Distributed under the MIT/X11 software license; see <a href="http://www.opensource.org/licenses/mit-license.php"><span style=" text-decoration: underline; color:#0000ff;">http://www.opensource.org/licenses/mit-license.php</span></a></p></body></html> - <html><head/><body><p>Distribuata laŭ la permesilo "MIT/X11 software license"; vidu <a href="http://www.opensource.org/licenses/mit-license.php"><span style=" text-decoration: underline; color:#0000ff;">http://www.opensource.org/licenses/mit-license.php</span></a></p></body></html> + <html><head/><body><p>Distribuata laŭ la permesilo “MIT/X11 software license”; legu <a href="http://www.opensource.org/licenses/mit-license.php"><span style=" text-decoration: underline; color:#0000ff;">http://www.opensource.org/licenses/mit-license.php</span></a></p></body></html> - + This is Beta software. Tio ĉi estas beta-eldono. @@ -1739,10 +1793,10 @@ La ‘hazardnombra’ adreso estas antaŭagordita, sed antaŭkalkuleblaj adresoj <html><head/><body><p>Copyright © 2012-2016 Jonathan Warren<br/>Copyright © 2013-2016 The Bitmessage Developers</p></body></html> - - - <html><head/><body><p>Copyright &copy; 2012-2016 Jonathan Warren<br/>Copyright &copy; 2013-2016 The Bitmessage Developers</p></body></html> - <html><head/><body><p>Kopirajto &copy; 2012-2016 Jonathan WARREN<br/>Kopirajto &copy; 2013-2016 La programistoj de Bitmesaĝo</p></body></html> + + + <html><head/><body><p>Copyright © 2012-2016 Jonathan Warren<br/>Copyright © 2013-2017 The Bitmessage Developers</p></body></html> + <html><head/><body><p>Kopirajto © 2012-2016 Jonathan WARREN<br/>Kopirajto © 2013-2017 La Programistoj de Bitmesaĝo</p></body></html> @@ -1786,40 +1840,45 @@ La ‘hazardnombra’ adreso estas antaŭagordita, sed antaŭkalkuleblaj adresoj connectDialog - + Bitmessage Bitmesaĝo - + Bitmessage won't connect to anyone until you let it. Bitmesaĝo ne konektos antaŭ vi permesos al ĝi. - + Connect now Konekti nun - + Let me configure special network settings first - Lasu min unue fari specialajn retajn agordojn + Ebligi al mi unue alĝustigi specialajn agordojn de reto + + + + Work offline + Funkcii eksterrete helpDialog - + Help Helpo - + <a href="https://bitmessage.org/wiki/PyBitmessage_Help">https://bitmessage.org/wiki/PyBitmessage_Help</a> <a href="https://bitmessage.org/wiki/PyBitmessage_Help">https://bitmessage.org/wiki/PyBitmessage_Help</a> - + As Bitmessage is a collaborative project, help can be found online in the Bitmessage Wiki: Ĉar Bitmesaĝo estas kunlabora projekto, vi povas trovi helpon enrete ĉe la vikio de Bitmesaĝo: @@ -1827,30 +1886,35 @@ La ‘hazardnombra’ adreso estas antaŭagordita, sed antaŭkalkuleblaj adresoj iconGlossaryDialog - + Icon Glossary Piktograma Glosaro - + You have no connections with other peers. Vi havas neniujn konektojn al aliaj samtavolanoj. - + You have made at least one connection to a peer using an outgoing connection but you have not yet received any incoming connections. Your firewall or home router probably isn't configured to forward incoming TCP connections to your computer. Bitmessage will work just fine but it would help the Bitmessage network if you allowed for incoming connections and will help you be a better-connected node. - Vi konektis almenaŭ al unu samtavolano uzante elirantan konekton, sed vi ankoraŭ ne ricevis enirantajn konektojn. Via fajroŝirmilo (firewall) aŭ hejma enkursigilo (router) verŝajne estas agordita por ne plusendi enirantajn TCP konektojn al via komputilo. Bitmesaĝo funkcios sufiĉe bone, sed helpus al la bitmesaĝa reto se vi permesus enirantajn konektojn kaj tiel estus pli bone konektita nodo. + Vi konektis almenaŭ al unu samtavolano uzante elirantan konekton, sed vi ankoraŭ ne ricevis enirantajn konektojn. Via fajroŝirmilo (firewall) aŭ hejma enkursigilo (router) verŝajne estas agordita por ne plusendi enirantajn TCP-konektojn al via komputilo. Bitmesaĝo funkcios sufiĉe bone, sed helpus al la bitmesaĝa reto se vi permesus enirantajn konektojn kaj tiel estus pli bone konektita nodo. You are using TCP port ?. (This can be changed in the settings). - Vi uzas TCP-pordon ?. (Ĝi estas ŝanĝebla en la agordoj). + - + You do have connections with other peers and your firewall is correctly configured. Vi havas konektojn al aliaj samtavolanoj kaj via fajroŝirmilo estas ĝuste agordita. + + + You are using TCP port %1. (This can be changed in the settings). + Vi uzas TCP-pordon %1 (tio ĉi estas ŝanĝebla en la agordoj). + networkstatus @@ -1860,37 +1924,37 @@ La ‘hazardnombra’ adreso estas antaŭagordita, sed antaŭkalkuleblaj adresoj Ĉiuj konektoj: - + Since startup: Ekde starto: - + Processed 0 person-to-person messages. Pritraktis 0 inter-personajn mesaĝojn. - + Processed 0 public keys. Pritraktis 0 publikajn ŝlosilojn. - + Processed 0 broadcasts. Pritraktis 0 elsendojn. - + Inventory lookups per second: 0 Petoj pri inventaro en sekundo: 0 - + Objects to be synced: Samtempigotaj eroj: - + Stream # Fluo # @@ -1900,112 +1964,112 @@ La ‘hazardnombra’ adreso estas antaŭagordita, sed antaŭkalkuleblaj adresoj - + Since startup on %1 Ekde lanĉo de la programo je %1 - + Down: %1/s Total: %2 Elŝuto: %1/s Sume: %2 - + Up: %1/s Total: %2 Alŝuto: %1/s Sume: %2 - + Total Connections: %1 Ĉiuj konektoj: %1 - + Inventory lookups per second: %1 Petoj pri inventaro en sekundo: %1 - + Up: 0 kB/s Alŝuto: 0 kB/s - + Down: 0 kB/s Elŝuto: 0 kB/s - + Network Status Reta stato - + byte(s) bitokobitokoj - + Object(s) to be synced: %n Objekto por samtempigi: %nObjektoj por samtempigi: %n - + Processed %n person-to-person message(s). Pritraktis %n inter-personan mesaĝon.Pritraktis %n inter-personajn mesaĝojn. - + Processed %n broadcast message(s). Pritraktis %n elsendon.Pritraktis %n elsendojn. - + Processed %n public key(s). Pritraktis %n publikan ŝlosilon.Pritraktis %n publikajn ŝlosilojn. - + Peer Samtavolano - + IP address or hostname IP-adreso aŭ gastiga nomo - + Rating Takso - + PyBitmessage tracks the success rate of connection attempts to individual nodes. The rating ranges from -1 to 1 and affects the likelihood of selecting the node in the future PyBitmessage registras frekvencon de sukcesaj konekt-provoj al aliaj nodoj. Takso varias de -1 ĝis 1 kaj influas probablon por elekti nodon estontece. - + User agent Klienta aplikaĵo - + Peer's self-reported software Anoncata klienta aplikaĵo - + TLS TLS - + Connection encryption Konekta ĉifrado - + List of streams negotiated between you and the peer Listo de interŝanĝataj fluoj inter vi kaj la samtavolano @@ -2064,7 +2128,7 @@ La ‘hazardnombra’ adreso estas antaŭagordita, sed antaŭkalkuleblaj adresoj - Chan passhphrase/name: + Chan passphrase/name: Kanala pasfrazo/nomo: @@ -2086,7 +2150,7 @@ La ‘hazardnombra’ adreso estas antaŭagordita, sed antaŭkalkuleblaj adresoj newchandialog - + Successfully created / joined chan %1 Sukcese kreis / aniĝis al la kanalo %1 @@ -2130,54 +2194,54 @@ La ‘hazardnombra’ adreso estas antaŭagordita, sed antaŭkalkuleblaj adresoj regenerateAddressesDialog - + Regenerate Existing Addresses Regeneri ekzistantajn adresojn - + Regenerate existing addresses Regeneri ekzistantajn adresojn - + Passphrase Pasfrazo - + Number of addresses to make based on your passphrase: Nombro da farotaj adresoj bazante sur via pasfrazo: - + Address version number: Numero de adresversio: - + Stream number: - Fluo numero: + Numero de fluo: - + 1 1 - + Spend several minutes of extra computing time to make the address(es) 1 or 2 characters shorter - Pasigi kelkajn minutojn aldone kompute por krei la adreso(j)n 1 aŭ 2 signoj pli mallongaj + Pasigi kelkajn minutojn aldone kompute por krei adreso(j)n mallongaj je 1 aŭ 2 signoj - + You must check (or not check) this box just like you did (or didn't) when you made your addresses the first time. - Vi devas marki (aŭ ne marki) ĉi tiun markobutonon samkiel vi faris kiam vi generis vian adreson unuafoje. + Vi devas marki (aŭ ne marki) tiun ĉi mark-butonon samkiel vi faris kiam vi generis vian adresojn unuafoje. - + If you have previously made deterministic addresses but lost them due to an accident (like hard drive failure), you can regenerate them here. If you used the random number generator to make your addresses then this form will be of no use to you. - Se vi antaŭe kreis antaŭkalkuleblajn (determinismajn) adresojn sed perdis ilin akcidente (ekz. en diska paneo), vi povas regeneri ilin ĉi tie. Se vi uzis la generilo de hazardnombroj por krei vian adreson tiu formularo ne taŭgos por vi. + Se vi antaŭe kreis antaŭkalkuleblajn (determinismajn) adresojn sed perdis ilin akcidente (ekz. en diska paneo), vi povas regeneri ilin tie ĉi. Se vi uzis la hazardnombran generilon por krei viajn adresojn, tiu ĉi formularo ne taŭgos por vi. @@ -2291,7 +2355,7 @@ La ‘hazardnombra’ adreso estas antaŭagordita, sed antaŭkalkuleblaj adresoj Proxy server / Tor - Prokurila (proxy) servilo / Tor + Retperanta (proxy) servilo / Tor @@ -2326,7 +2390,7 @@ La ‘hazardnombra’ adreso estas antaŭagordita, sed antaŭkalkuleblaj adresoj Listen for incoming connections when using proxy - Aŭskulti pri alvenaj konektoj kiam dum uzado de prokurilo + Aŭskulti pri alvenaj konektoj kiam dum uzado de retperanto From 283696a05331e4a76f96531eec33e23cbb157b93 Mon Sep 17 00:00:00 2001 From: PyBitmessage Translations Date: Thu, 1 Feb 2018 20:19:08 +0100 Subject: [PATCH 366/407] Auto-updated language pl from transifex --- src/translations/bitmessage_pl.qm | Bin 91798 -> 90637 bytes src/translations/bitmessage_pl.ts | 758 ++++++++++++++++-------------- 2 files changed, 411 insertions(+), 347 deletions(-) diff --git a/src/translations/bitmessage_pl.qm b/src/translations/bitmessage_pl.qm index 1b51288030ccc843e1f67e161a1c72f4791723d1..3b9a0dcc6170060a14593fb1df4a4daac69cf226 100644 GIT binary patch delta 4475 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 YzsR9OP<%{^E;S}SJ-*DtnceOCFT;wrl>h($ diff --git a/src/translations/bitmessage_pl.ts b/src/translations/bitmessage_pl.ts index c0025336..c97cb4bb 100644 --- a/src/translations/bitmessage_pl.ts +++ b/src/translations/bitmessage_pl.ts @@ -2,17 +2,17 @@ AddAddressDialog - + Add new entry - Dodaj adres + Dodaj nowy wpis - + Label - Nazwa + Etykieta - + Address Adres @@ -20,70 +20,99 @@ EmailGatewayDialog - + Email gateway Przekaźnik e-mail - + Register on email gateway Zarejestruj u przekaźnika e-mail - + Account status at email gateway Status konta u przekaźnika e-mail - + Change account settings at email gateway Zmień ustawienia konta u przekaźnika e-mail - + Unregister from email gateway Wyrejestruj od przekaźnika e-mail - + Email gateway allows you to communicate with email users. Currently, only the Mailchuck email gateway (@mailchuck.com) is available. Przekaźnik e-mail umożliwia komunikację z użytkownikami poczty elektronicznej. Obecnie usługa ta oferowana jest tylko przez Mailchuck (@mailchuck.com). - + Desired email address (including @mailchuck.com): Wybrany adres e-mail (razem a końcówką @mailchuck.com): + + + @mailchuck.com + @mailchuck.com + + + + Registration failed: + Rejestracja nie powiodła się: + + + + The requested email address is not available, please try a new one. + Wybrany adres e-mail nie jest dostępny, proszę spróbować inny. + + + + Sending email gateway registration request + Wysyłanie zapytania o rejestrację na bramce poczty + + + + Sending email gateway unregistration request + Wysyłanie zapytania o wyrejestrowanie z bramki poczty + + + + Sending email gateway status request + Wysyłanie zapytania o stan bramki poczty + EmailGatewayRegistrationDialog - + Registration failed: - Rejestracja nie powiodła się: + - + The requested email address is not available, please try a new one. Fill out the new desired email address (including @mailchuck.com) below: - Wybrany adres e-mail nie jest dostępny, proszę spróbować inny. Wpisz adres poniżej (razem z końcówką @mailchuck.com): + Email gateway registration - Rejestracja u przekaźnika e-mail + Email gateway allows you to communicate with email users. Currently, only the Mailchuck email gateway (@mailchuck.com) is available. Please type the desired email address (including @mailchuck.com) below: - Przekaźnik e-mail umożliwia komunikację z użytkownikami e-maili. Obecnie usługa ta oferowana jest tylko przez bramkę Mailchuck (@mailchuck.com). -Wprowadź wybrany adres e-mail (razem z końcówką @mailchuck.com) poniżej: + Mailchuck - + # You can use this to configure your email gateway account # Uncomment the setting you want to use # Here are the options: @@ -170,52 +199,52 @@ Wprowadź wybrany adres e-mail (razem z końcówką @mailchuck.com) poniżej: MainWindow - + Reply to sender Odpowiedz do nadawcy - + Reply to channel Odpowiedz do kanału - + Add sender to your Address Book Dodaj nadawcę do Książki Adresowej - + Add sender to your Blacklist Dodaj nadawcę do Listy Blokowanych - + Move to Trash Przenieś do kosza - + Undelete Przywróć - + View HTML code as formatted text Wyświetl kod HTML w postaci sformatowanej - + Save message as... Zapisz wiadomość jako… - + Mark Unread Oznacz jako nieprzeczytane - + New Nowe @@ -240,12 +269,12 @@ Wprowadź wybrany adres e-mail (razem z końcówką @mailchuck.com) poniżej:Kopiuj adres do schowka - + Special address behavior... Specjalne zachowanie adresu… - + Email gateway Przekaźnik e-mail @@ -255,37 +284,37 @@ Wprowadź wybrany adres e-mail (razem z końcówką @mailchuck.com) poniżej:Usuń - + Send message to this address Wyślij wiadomość pod ten adres - + Subscribe to this address Subskrybuj ten adres - + Add New Address Dodaj nowy adres - + Copy destination address to clipboard Kopiuj adres odbiorcy do schowka - + Force send Wymuś wysłanie - + One of your addresses, %1, is an old version 1 address. Version 1 addresses are no longer supported. May we delete it now? Jeden z adresów, %1, jest starym adresem wersji 1. Adresy tej wersji nie są już wspierane. Usunąć go? - + Waiting for their encryption key. Will request it again soon. Oczekiwanie na klucz szyfrujący odbiorcy. Niedługo nastąpi ponowne wysłanie o niego prośby. @@ -295,17 +324,17 @@ Wprowadź wybrany adres e-mail (razem z końcówką @mailchuck.com) poniżej: - + Queued. W kolejce do wysłania. - + Message sent. Waiting for acknowledgement. Sent at %1 Wiadomość wysłana. Oczekiwanie na potwierdzenie odbioru. Wysłano o %1 - + Message sent. Sent at %1 Wiadomość wysłana. Wysłano o %1 @@ -315,77 +344,77 @@ Wprowadź wybrany adres e-mail (razem z końcówką @mailchuck.com) poniżej: - + Acknowledgement of the message received %1 Otrzymano potwierdzenie odbioru wiadomości %1 - + Broadcast queued. Przekaz w kolejce do wysłania. - + Broadcast on %1 Wysłana o %1 - + Problem: The work demanded by the recipient is more difficult than you are willing to do. %1 Problem: dowód pracy wymagany przez odbiorcę jest trudniejszy niż zaakceptowany przez Ciebie. %1 - + Problem: The recipient's encryption key is no good. Could not encrypt message. %1 Problem: klucz szyfrujący odbiorcy jest nieprawidłowy. Nie można zaszyfrować wiadomości. %1 - + Forced difficulty override. Send should start soon. Wymuszono ominięcie trudności. Wysłanie zostanie wkrótce rozpoczęte. - + Unknown status: %1 %2 Nieznany status: %1 %2 - + Not Connected Brak połączenia - + Show Bitmessage Pokaż Bitmessage - + Send Wyślij - + Subscribe Subskrybuj - + Channel Kanał - + Quit Zamknij - + You may manage your keys by editing the keys.dat file stored in the same directory as this program. It is important that you back up this file. Możesz zarządzać swoimi kluczami edytując plik keys.dat znajdujący się w tym samym katalogu co program. Zaleca się zrobienie kopii zapasowej tego pliku. - + You may manage your keys by editing the keys.dat file stored in %1 It is important that you back up this file. @@ -394,17 +423,17 @@ It is important that you back up this file. Zaleca się zrobienie kopii zapasowej tego pliku. - + Open keys.dat? Otworzyć plik keys.dat? - + You may manage your keys by editing the keys.dat file stored in the same directory as this program. It is important that you back up this file. Would you like to open the file now? (Be sure to close Bitmessage before making any changes.) Możesz zarządzać swoimi kluczami edytując plik keys.dat znajdujący się w tym samym katalogu co program. Zaleca się zrobienie kopii zapasowej tego pliku. Czy chcesz otworzyć ten plik teraz? (Zamknij Bitmessage, przed wprowadzeniem jakichkolwiek zmian.) - + You may manage your keys by editing the keys.dat file stored in %1 It is important that you back up this file. Would you like to open the file now? (Be sure to close Bitmessage before making any changes.) @@ -413,37 +442,37 @@ It is important that you back up this file. Would you like to open the file now? Zaleca się zrobienie kopii zapasowej tego pliku. Czy chcesz otworzyć ten plik teraz? (Zamknij Bitmessage przed wprowadzeniem jakichkolwiek zmian.) - + Delete trash? Opróżnić kosz? - + Are you sure you want to delete all trashed messages? Czy na pewno usunąć wszystkie wiadomości z kosza? - + bad passphrase nieprawidłowe hasło - + You must type your passphrase. If you don't have one then this is not the form for you. Musisz wpisać swoje hasło. Jeżeli go nie posiadasz, to ten formularz nie jest dla Ciebie. - + Bad address version number Nieprawidłowy numer wersji adresu - + Your address version number must be a number: either 3 or 4. Twój numer wersji adresu powinien wynosić: 3 lub 4. - + Your address version number must be either 3 or 4. Twój numer wersji adresu powinien wynosić: 3 lub 4. @@ -513,22 +542,22 @@ Zaleca się zrobienie kopii zapasowej tego pliku. Czy chcesz otworzyć ten plik - + Connection lost Połączenie utracone - + Connected Połączono - + Message trashed Wiadomość usunięta - + The TTL, or Time-To-Live is the length of time that the network will hold the message. The recipient must get it during this time. If your Bitmessage client does not hear an acknowledgement, it will resend the message automatically. The longer the Time-To-Live, the @@ -539,17 +568,17 @@ Im dłuższy TTL, tym więcej pracy będzie musiał wykonac komputer wysyłając Zwykle 4-5 dniowy TTL jest odpowiedni. - + Message too long Wiadomość zbyt długa - + The message that you are trying to send is too long by %1 bytes. (The maximum is 261644 bytes). Please cut it down before sending. Wiadomość jest za długa o %1 bajtów (maksymalna długość wynosi 261644 bajty). Przed wysłaniem należy ją skrócić. - + Error: Your account wasn't registered at an email gateway. Sending registration now as %1, please wait for the registration to be processed before retrying sending. Błąd: Twoje konto nie było zarejestrowane w bramce poczty. Rejestrowanie jako %1, proszę poczekać na zakończenie procesu przed ponowną próbą wysłania wiadomości. @@ -594,57 +623,57 @@ Zwykle 4-5 dniowy TTL jest odpowiedni. - + Error: You must specify a From address. If you don't have one, go to the 'Your Identities' tab. Błąd: musisz wybrać adres wysyłania. Jeżeli go nie posiadasz, przejdź do zakładki 'Twoje tożsamości'. - + Address version number Numer wersji adresu - + Concerning the address %1, Bitmessage cannot understand address version numbers of %2. Perhaps upgrade Bitmessage to the latest version. Odnośnie adresu %1, Bitmessage nie potrafi odczytać wersji adresu %2. Może uaktualnij Bitmessage do najnowszej wersji. - + Stream number Numer strumienia - + Concerning the address %1, Bitmessage cannot handle stream numbers of %2. Perhaps upgrade Bitmessage to the latest version. Odnośnie adresu %1, Bitmessage nie potrafi operować na strumieniu adresu %2. Może uaktualnij Bitmessage do najnowszej wersji. - + Warning: You are currently not connected. Bitmessage will do the work necessary to send the message but it won't send until you connect. Uwaga: nie jesteś obecnie połączony. Bitmessage wykona niezbędną pracę do wysłania wiadomości, ale nie wyśle jej póki się nie połączysz. - + Message queued. W kolejce do wysłania - + Your 'To' field is empty. Pole 'Do' jest puste - + Right click one or more entries in your address book and select 'Send message to this address'. Użyj prawego przycisku myszy na adresie z książki adresowej i wybierz opcję "Wyślij wiadomość do tego adresu". - + Fetched address from namecoin identity. Pobrano adres z identyfikatora Namecoin. - + New Message Nowa wiadomość @@ -654,9 +683,9 @@ Zwykle 4-5 dniowy TTL jest odpowiedni. - + Sending email gateway registration request - Wysyłanie zapytania o rejestrację na bramce poczty + @@ -669,142 +698,142 @@ Zwykle 4-5 dniowy TTL jest odpowiedni. Wprowadzono niewłaściwy adres, który został zignorowany. - + Error: You cannot add the same address to your address book twice. Try renaming the existing one if you want. Błąd: Adres znajduje się już w książce adresowej. - + Error: You cannot add the same address to your subscriptions twice. Perhaps rename the existing one if you want. Błąd: Adres znajduje się już na liście subskrybcji. - + Restart Uruchom ponownie - + You must restart Bitmessage for the port number change to take effect. Musisz zrestartować Bitmessage, aby zmiana numeru portu weszła w życie. - + Bitmessage will use your proxy from now on but you may want to manually restart Bitmessage now to close existing connections (if any). Bitmessage będzie of teraz korzystał z serwera proxy, ale możesz ręcznie zrestartować Bitmessage, aby zamknąć obecne połączenia (jeżeli występują). - + Number needed Wymagany numer - + Your maximum download and upload rate must be numbers. Ignoring what you typed. Maksymalne prędkości wysyłania i pobierania powinny być liczbami. Zignorowano zmiany. - + Will not resend ever Nigdy nie wysyłaj ponownie - + Note that the time limit you entered is less than the amount of time Bitmessage waits for the first resend attempt therefore your messages will never be resent. Zauważ, że wpisany limit czasu wynosi mniej niż czas, który Bitmessage czeka przed pierwszą ponowną próbą wysłania wiadomości, więc Twoje wiadomości nie zostaną nigdy wysłane ponownie. - + Sending email gateway unregistration request - Wysyłanie zapytania o wyrejestrowanie z bramki poczty + - + Sending email gateway status request - Wysyłanie zapytania o stan bramki poczty + - + Passphrase mismatch Hasła różnią się - + The passphrase you entered twice doesn't match. Try again. Hasła, które wpisałeś nie pasują. Spróbuj ponownie. - + Choose a passphrase Wpisz hasło - + You really do need a passphrase. Naprawdę musisz wpisać hasło. - + Address is gone Adres zniknął - + Bitmessage cannot find your address %1. Perhaps you removed it? Bitmessage nie może odnaleźć Twojego adresu %1. Może go usunąłeś? - + Address disabled Adres nieaktywny - + Error: The address from which you are trying to send is disabled. You'll have to enable it on the 'Your Identities' tab before using it. Błąd: adres, z którego próbowałeś wysłać wiadomość jest nieaktywny. Włącz go w zakładce 'Twoje tożsamości' zanim go użyjesz. - + Entry added to the Address Book. Edit the label to your liking. - Dodano wpis do książki adresowej. Można teraz zmienić jego nazwę. + - + Entry added to the blacklist. Edit the label to your liking. Dodano wpis do listy blokowanych. Można teraz zmienić jego nazwę. - + Error: You cannot add the same address to your blacklist twice. Try renaming the existing one if you want. Błąd: Adres znajduje się już na liście blokowanych. - + Moved items to trash. Przeniesiono wiadomości do kosza. - + Undeleted item. - Przywróć wiadomość. + Przywrócono wiadomość. - + Save As... Zapisz jako… - + Write error. Błąd zapisu. - + No addresses selected. Nie wybrano adresu. - + If you delete the subscription, messages that you already received will become inaccessible. Maybe you can consider disabling the subscription instead. Disabled subscriptions will not receive new messages, but you can still view messages you already received. Are you sure you want to delete the subscription? @@ -813,7 +842,7 @@ Are you sure you want to delete the subscription? Czy na pewno chcesz usunąć tę subskrypcję? - + If you delete the channel, messages that you already received will become inaccessible. Maybe you can consider disabling the channel instead. Disabled channels will not receive new messages, but you can still view messages you already received. Are you sure you want to delete the channel? @@ -822,277 +851,277 @@ Are you sure you want to delete the channel? Czy na pewno chcesz usunąć ten kanał? - + Do you really want to remove this avatar? Czy na pewno chcesz usunąć ten awatar? - + You have already set an avatar for this address. Do you really want to overwrite it? Już ustawiłeś awatar dla tego adresu. Czy na pewno chcesz go nadpisać? - + Start-on-login not yet supported on your OS. Start po zalogowaniu jeszcze nie jest wspierany pod Twoim systemem. - + Minimize-to-tray not yet supported on your OS. Minimalizacja do zasobnika nie jest jeszcze wspierana pod Twoim systemem. - + Tray notifications not yet supported on your OS. Powiadomienia w zasobniku nie są jeszcze wspierane pod Twoim systemem. - + Testing... Testowanie… - + This is a chan address. You cannot use it as a pseudo-mailing list. - To jest adres kanału. Nie możesz go użyć jako pseudo-listy-dyskusyjnej. + - + The address should start with ''BM-'' - Adres powinien zaczynać sie od "BM-" + Adres powinien zaczynać się od „BM-” - + The address is not typed or copied correctly (the checksum failed). Adres nie został skopiowany lub przepisany poprawnie (błąd sumy kontrolnej). - + The version number of this address is higher than this software can support. Please upgrade Bitmessage. Numer wersji tego adresu jest wyższy niż ten program może obsłużyć. Proszę zaktualizować Bitmessage. - + The address contains invalid characters. Adres zawiera nieprawidłowe znaki. - + Some data encoded in the address is too short. Niektóre dane zakodowane w adresie są za krótkie. - + Some data encoded in the address is too long. Niektóre dane zakodowane w adresie są za długie. - + Some data encoded in the address is malformed. Niektóre dane zakodowane w adresie są uszkodzone. - + Enter an address above. - Wprowadź adres powyżej. + - + Address is an old type. We cannot display its past broadcasts. - Adres starego typu + Adres starego typu. Nie można wyświetlić jego wiadomości subskrypcji. - + There are no recent broadcasts from this address to display. - Brak niedawnych wiadomości przekazów do wyświetlenia. + Brak niedawnych wiadomości subskrypcji do wyświetlenia. - + You are using TCP port %1. (This can be changed in the settings). - Btimessage używa portu TCP %1. (Można go zmienić w ustawieniach). + - + Bitmessage Bitmessage - + Identities Tożsamości - + New Identity Nowa tożsamość - + Search Szukaj - + All Wszystkie - + To Do - + From Od - + Subject Temat - + Message Wiadomość - + Received Odebrana - + Messages Wiadomości - + Address book Książka adresowa - + Address Adres - + Add Contact Dodaj kontakt - + Fetch Namecoin ID Pobierz Namecoin ID - + Subject: Temat: - + From: Od: - + To: Do: - + Send ordinary Message Wyślij zwykłą wiadomość - + Send Message to your Subscribers Wyślij wiadomość broadcast - + TTL: Czas życia: - + Subscriptions Subskrypcje - + Add new Subscription Dodaj subskrypcję - + Chans Kanały - + Add Chan Dodaj kanał - + File Plik - + Settings Ustawienia - + Help Pomoc - + Import keys Importuj klucze - + Manage keys Zarządzaj kluczami - + Ctrl+Q Ctrl+Q - + F1 F1 - + Contact support Kontakt z twórcami - + About O programie - + Regenerate deterministic addresses Odtwórz adres deterministyczny - + Delete all trashed messages Usuń wiadomości z kosza - + Join / Create chan Dołącz / Utwórz kanał @@ -1117,67 +1146,67 @@ Czy na pewno chcesz usunąć ten kanał? Dodaj nowy wpis - + Display the %1 recent broadcast(s) from this address. - Pokaż %1 ostatnich wiadomości przekazów z tego adresu. + Wyświetl %1 ostatnich wiadomości subskrypcji z tego adresu. - + New version of PyBitmessage is available: %1. Download it from https://github.com/Bitmessage/PyBitmessage/releases/latest Nowa wersja Bitmessage jest dostępna: %1. Pobierz ją z https://github.com/Bitmessage/PyBitmessage/releases/latest - + Waiting for PoW to finish... %1% Oczekiwanie na wykonanie dowodu pracy… %1% - + Shutting down Pybitmessage... %1% Zamykanie PyBitmessage… %1% - + Waiting for objects to be sent... %1% Oczekiwanie na wysłanie obiektów… %1% - + Saving settings... %1% Zapisywanie ustawień… %1% - + Shutting down core... %1% Zamykanie rdzenia programu… %1% - + Stopping notifications... %1% Zatrzymywanie powiadomień… %1% - + Shutdown imminent... %1% Zaraz zamknę… %1% - + %n hour(s) %n godzina%n godziny%n godzin%n godzin - + %n day(s) %n dzień%n dni%n dni%n dni - + Shutting down PyBitmessage... %1% Zamykanie PyBitmessage… %1% - + Sent Wysłane @@ -1222,86 +1251,86 @@ Czy na pewno chcesz usunąć ten kanał? Uwaga: Twój dysk lub partycja jest pełny. Bitmessage zamknie się. - + Error! Could not find sender address (your address) in the keys.dat file. Błąd! Nie można odnaleźć adresu nadawcy (Twojego adresu) w pliku keys.dat. - + Doing work necessary to send broadcast... Wykonywanie dowodu pracy niezbędnego do wysłania przekazu… - + Broadcast sent on %1 - Przekaz wysłane o %1 + Wysłano: %1 - + Encryption key was requested earlier. Prośba o klucz szyfrujący została już wysłana. - + Sending a request for the recipient's encryption key. Wysyłanie zapytania o klucz szyfrujący odbiorcy. - + Looking up the receiver's public key Wyszukiwanie klucza publicznego odbiorcy - + Problem: Destination is a mobile device who requests that the destination be included in the message but this is disallowed in your settings. %1 Problem: adres docelowy jest urządzeniem przenośnym, które wymaga, aby adres docelowy był zawarty w wiadomości, ale jest to zabronione w Twoich ustawieniach. %1 - + Doing work necessary to send message. There is no required difficulty for version 2 addresses like this. Wykonywanie dowodu pracy niezbędnego do wysłania wiadomości. Nie ma wymaganej trudności dla adresów w wersji 2, takich jak ten adres. - + Doing work necessary to send message. Receiver's required difficulty: %1 and %2 Wykonywanie dowodu pracy niezbędnego do wysłania wiadomości. Odbiorca wymaga trudności: %1 i %2 - + Problem: The work demanded by the recipient (%1 and %2) is more difficult than you are willing to do. %3 Problem: dowód pracy wymagany przez odbiorcę (%1 i %2) jest trudniejszy niż chciałbyś wykonać. %3 - + Problem: You are trying to send a message to yourself or a chan but your encryption key could not be found in the keys.dat file. Could not encrypt message. %1 Problem: próbujesz wysłać wiadomość do siebie lub na kanał, ale Twój klucz szyfrujący nie został znaleziony w pliku keys.dat. Nie można zaszyfrować wiadomości. %1 - + Doing work necessary to send message. Wykonywanie pracy potrzebnej do wysłania wiadomości. - + Message sent. Waiting for acknowledgement. Sent on %1 Wiadomość wysłana. Oczekiwanie na potwierdzenie odbioru. Wysłano o %1 - + Doing work necessary to request encryption key. Wykonywanie pracy niezbędnej do prośby o klucz szyfrujący. - + Broadcasting the public key request. This program will auto-retry if they are offline. Rozsyłanie prośby o klucz publiczny. Program spróbuje ponownie, jeżeli jest on niepołączony. - + Sending public key request. Waiting for reply. Requested at %1 Wysyłanie prośby o klucz publiczny. Oczekiwanie na odpowiedź. Zapytano o %1 @@ -1316,37 +1345,37 @@ Odbiorca wymaga trudności: %1 i %2 Usunięto mapowanie portów UPnP - + Mark all messages as read Oznacz wszystkie jako przeczytane - + Are you sure you would like to mark all messages read? Czy na pewno chcesz oznaczyć wszystkie wiadomości jako przeczytane? - + Doing work necessary to send broadcast. Wykonywanie dowodu pracy niezbędnego do wysłania przekazu. - + Proof of work pending Dowód pracy zawieszony - + %n object(s) pending proof of work Zawieszony dowód pracy %n obiektuZawieszony dowód pracy %n obiektówZawieszony dowód pracy %n obiektówZawieszony dowód pracy %n obiektów - + %n object(s) waiting to be distributed %n obiekt oczekuje na wysłanie%n obiektów oczekuje na wysłanie%n obiektów oczekuje na wysłanie%n obiektów oczekuje na wysłanie - + Wait until these tasks finish? Czy poczekać aż te zadania zostaną zakończone? @@ -1411,12 +1440,12 @@ Odbiorca wymaga trudności: %1 i %2 Twoje procesory graficzne nie obliczyły poprawnie, wyłączam OpenCL. Prosimy zaraportować przypadek twórcom programu. - + Set notification sound... Ustaw dźwięk powiadomień… - + Welcome to easy and secure Bitmessage * send messages to other people @@ -1430,120 +1459,140 @@ Witamy w przyjaznym i bezpiecznym Bitmessage * dyskutuj na kanałach (chany) z innymi ludźmi - + not recommended for chans niezalecany dla kanałów - + + Quiet Mode + Tryb cichy + + + Problems connecting? Try enabling UPnP in the Network Settings Problem z połączeniem? Spróbuj włączyć UPnP w ustawieniach sieci. - + You are trying to send an email instead of a bitmessage. This requires registering with a gateway. Attempt to register? Próbujesz wysłać e-mail zamiast wiadomość bitmessage. To wymaga zarejestrowania się na bramce. Czy zarejestrować? - + Error: Bitmessage addresses start with BM- Please check the recipient address %1 Błąd: adresy Bitmessage zaczynają się od BM-. Proszę sprawdzić adres odbiorcy %1. - + Error: The recipient address %1 is not typed or copied correctly. Please check it. Błąd: adres odbiorcy %1 nie został skopiowany lub przepisany poprawnie. Proszę go sprawdzić. - + Error: The recipient address %1 contains invalid characters. Please check it. Błąd: adres odbiorcy %1 zawiera nieprawidłowe znaki. Proszę go sprawdzić. - + Error: The version of the recipient address %1 is too high. Either you need to upgrade your Bitmessage software or your acquaintance is being clever. Błąd: wersja adresu odbiorcy %1 jest za wysoka. Musisz albo zaktualizować Twoje oprogramowanie Bitmessage, albo twój znajomy Cię trolluje. - + Error: Some data encoded in the recipient address %1 is too short. There might be something wrong with the software of your acquaintance. Błąd: niektóre dane zakodowane w adresie odbiorcy %1 są zbyt krótkie. Być może coś nie działa należycie w programie Twojego znajomego. - + Error: Some data encoded in the recipient address %1 is too long. There might be something wrong with the software of your acquaintance. Błąd: niektóre dane zakodowane w adresie odbiorcy %1 są zbyt długie. Być może coś nie działa należycie w programie Twojego znajomego. - + Error: Some data encoded in the recipient address %1 is malformed. There might be something wrong with the software of your acquaintance. Błąd: niektóre dane zakodowane w adresie odbiorcy %1 są uszkodzone. Być może coś nie działa należycie w programie Twojego znajomego. - + Error: Something is wrong with the recipient address %1. Błąd: coś jest nie tak z adresem odbiorcy %1. - + + Error: %1 + Błąd: %1 + + + From %1 Od %1 - + Synchronisation pending Synchronizacja zawieszona - + Bitmessage hasn't synchronised with the network, %n object(s) to be downloaded. If you quit now, it may cause delivery delays. Wait until the synchronisation finishes? Bitmessage nie zsynchronizował się z siecią, %n obiekt oczekuje na pobranie. Jeżeli zamkniesz go teraz, może to spowodować opóźnienia dostarczeń. Czy poczekać na zakończenie synchronizacji?Bitmessage nie zsynchronizował się z siecią, %n obiekty oczekują na pobranie. Jeżeli zamkniesz go teraz, może to spowodować opóźnienia dostarczeń. Czy poczekać na zakończenie synchronizacji?Bitmessage nie zsynchronizował się z siecią, %n obiektów oczekuje na pobranie. Jeżeli zamkniesz go teraz, może to spowodować opóźnienia dostarczeń. Czy poczekać na zakończenie synchronizacji?Bitmessage nie zsynchronizował się z siecią, %n obiektów oczekuje na pobranie. Jeżeli zamkniesz go teraz, może to spowodować opóźnienia dostarczeń. Czy poczekać na zakończenie synchronizacji? - + Not connected Niepołączony - + Bitmessage isn't connected to the network. If you quit now, it may cause delivery delays. Wait until connected and the synchronisation finishes? Bitmessage nie połączył się z siecią. Jeżeli zamkniesz go teraz, może to spowodować opóźnienia dostarczeń. Czy poczekać na połączenie i zakończenie synchronizacji? - + Waiting for network connection... Oczekiwanie na połączenie sieciowe… - + Waiting for finishing synchronisation... Oczekiwanie na zakończenie synchronizacji… - + You have already set a notification sound for this address book entry. Do you really want to overwrite it? Już ustawiłeś dźwięk powiadomienia dla tego kontaktu. Czy chcesz go zastąpić? + + + Go online + Połącz + + + + Go offline + Rozłącz + MessageView - + Follow external link Otwórz zewnętrzne łącze - + The link "%1" will open in a browser. It may be a security risk, it could de-anonymise you or download malicious data. Are you sure? Odnośnik "%1" zostanie otwarty w przeglądarce. Może to spowodować zagrożenie bezpieczeństwa, może on ujawnić Twoją anonimowość lub pobrać złośliwe dane. Czy jesteś pewien? - + HTML detected, click here to display Wykryto HTML, kliknij tu, aby go wyświetlić - + Click here to disable HTML Kliknij tutaj aby wyłączyć HTML @@ -1566,178 +1615,183 @@ Prawdopodobnie powinieneś zaktualizować Bitmessage. NewAddressDialog - + Create new Address Generuj nowy adres - + Here you may generate as many addresses as you like. Indeed, creating and abandoning addresses is encouraged. You may generate addresses by using either random numbers or by using a passphrase. If you use a passphrase, the address is called a "deterministic" address. The 'Random Number' option is selected by default but deterministic addresses have several pros and cons: - Tutaj możesz utworzyć tyle adresów ile tylko potrzebujesz. W istocie, tworzenie nowych i porzucanie adresów jest zalecane. Możesz wygenerować adres używając albo losowych liczb albo hasła. Jeżeli użyjesz hasła, adres taki jest nazywany adresem 'deterministycznym'. -Generowanie adresów 'losowych' jest wybrane domyślnie, jednak deterministyczne adresy mają swoje wady i zalety: + Tutaj możesz utworzyć tyle adresów, ile tylko potrzebujesz. W istocie, tworzenie nowych i porzucanie adresów jest zalecane. Możesz wygenerować adres używając albo losowych liczb albo hasła. Jeżeli użyjesz hasła, adres taki jest nazywany adresem „deterministycznym”. +Generowanie adresów „losowych” jest wybrane domyślnie, jednak deterministyczne adresy mają swoje wady i zalety: - + <html><head/><body><p><span style=" font-weight:600;">Pros:<br/></span>You can recreate your addresses on any computer from memory. <br/>You need-not worry about backing up your keys.dat file as long as you can remember your passphrase. <br/><span style=" font-weight:600;">Cons:<br/></span>You must remember (or write down) your passphrase if you expect to be able to recreate your keys if they are lost. <br/>You must remember the address version number and the stream number along with your passphrase. <br/>If you choose a weak passphrase and someone on the Internet can brute-force it, they can read your messages and send messages as you.</p></body></html> - <html><head/><body><p><span style=" font-weight:600;">Zalety:<br/></span>Możesz wygenerować swój adres na każdym komputerze 'z głowy'.<br/>Nie musisz się martwić o tworzenie kopii zapasowej pliku keys.dat tak długo jak pamiętasz hasło.</br><span style=" font-weight:600;">Wady:<br/></span>Musisz zapamiętać (będź zapisać) swoje hasło, jeżeli chcesz odzyskać swój adres po utracie kluczy.</br>Musisz zapamiętać numer wersji adresu i numer strumienia razem z hasłem.</br>Jeżeli użyjesz słabego hasła, ktoś z Internetu może je odgadnąć i przeczytać wszystkie Twoje wiadomości i wysyłać wiadomości jako Ty.</p></body></html> + <html><head/><body><p><span style=" font-weight:600;">Zalety:<br/></span>Możesz wygenerować swój adres na każdym komputerze z głowy.<br/>Nie musisz się martwić o tworzenie kopii zapasowej pliku keys.dat tak długo jak pamiętasz hasło.</br><span style=" font-weight:600;">Wady:<br/></span>Musisz zapamiętać (bądź zapisać) swoje hasło, jeżeli chcesz odzyskać swój adres po utracie kluczy.</br>Musisz zapamiętać numer wersji adresu i numer strumienia razem z hasłem.</br>Jeżeli użyjesz słabego hasła, ktoś z Internetu może je odgadnąć i przeczytać wszystkie Twoje wiadomości i wysyłać wiadomości jako Ty.</p></body></html> - + Use a random number generator to make an address Użyj generatora liczb losowych do utworzenia adresu - + Use a passphrase to make addresses Użyj hasła do utworzenia adresu - + Spend several minutes of extra computing time to make the address(es) 1 or 2 characters shorter Dołóż kilka minut dodatkowych obliczeń aby wygenerować adres(y) krótsze o 1 lub 2 znaki - + Make deterministic addresses Utwórz adres deterministyczny - + Address version number: 4 Numer wersji adresu: 4 - + In addition to your passphrase, you must remember these numbers: Razem ze swoim hasłem musisz zapamiętać te liczby: - + Passphrase Hasło - + Number of addresses to make based on your passphrase: Liczba adresów do wygenerowanie na podstawie hasła: - + Stream number: 1 Numer strumienia: 1 - + Retype passphrase Hasło ponownie - + Randomly generate address Adres losowy - + Label (not shown to anyone except you) Etykieta (nie wyświetlana komukolwiek oprócz Ciebie) - + Use the most available stream Użyj najbardziej dostępnego strumienia - + (best if this is the first of many addresses you will create) (zalecane, jeżeli jest to pierwszy z adresów który chcesz utworzyć) - + Use the same stream as an existing address Użyj tego samego strumienia co istniejący adres - + (saves you some bandwidth and processing power) - (oszczędza trochę transferu i procesora) + (oszczędza trochę transferu i mocy procesora) NewSubscriptionDialog - + Add new entry - Dodaj subskrypcję + Dodaj nowy wpis - + Label Etykieta - + Address Adres - + Enter an address above. - Wpisz adres powyżej. + Wprowadź adres powyżej. SpecialAddressBehaviorDialog - + Special Address Behavior Specjalne zachowanie adresu - + Behave as a normal address - Zachowuj się jako normalny adres + Zachowuj się jak normalny adres - + Behave as a pseudo-mailing-list address - Zachowuj się jako pseudo-lista-dyskusyjna + Zachowuj się jak pseudo-lista-dyskusyjna - + Mail received to a pseudo-mailing-list address will be automatically broadcast to subscribers (and thus will be public). Wiadomości wysłane na pseudo-listę-dyskusyjną zostaną automatycznie rozesłane do abonentów (i w ten sposób będą publiczne). - + Name of the pseudo-mailing-list: Nazwa pseudo-listy-dyskusyjnej: + + + This is a chan address. You cannot use it as a pseudo-mailing list. + To jest adres kanału. Nie możesz go użyć jako pseudo-listy-dyskusyjnej. + aboutDialog - + About O programie - + PyBitmessage - PyBitmessage + - + version ? - wersja ? + - + <html><head/><body><p>Distributed under the MIT/X11 software license; see <a href="http://www.opensource.org/licenses/mit-license.php"><span style=" text-decoration: underline; color:#0000ff;">http://www.opensource.org/licenses/mit-license.php</span></a></p></body></html> <html><head/><body><p>Rozpowszechniane na licencji MIT/X11 software license; zobacz <a href="http://www.opensource.org/licenses/mit-license.php"><span style=" text-decoration: underline; color:#0000ff;">http://www.opensource.org/licenses/mit-license.php</span></a></p></body></html> - + This is Beta software. To jest wersja Beta. @@ -1746,10 +1800,10 @@ Generowanie adresów 'losowych' jest wybrane domyślnie, jednak determ <html><head/><body><p>Copyright © 2012-2016 Jonathan Warren<br/>Copyright © 2013-2016 The Bitmessage Developers</p></body></html> - - - <html><head/><body><p>Copyright &copy; 2012-2016 Jonathan Warren<br/>Copyright &copy; 2013-2016 The Bitmessage Developers</p></body></html> - <html><head/><body><p>Prawa autorskie &copy; 2012-2016 Jonathan Warren<br/>Prawa autorskie &copy; 2013-2016 Programiści Bitmessage</p></body></html> + + + <html><head/><body><p>Copyright © 2012-2016 Jonathan Warren<br/>Copyright © 2013-2017 The Bitmessage Developers</p></body></html> + <html><head/><body><p>Prawa autorskie © 2012-2016 Jonathan Warren<br/>Prawa autorskie © 2013-2017 Programiści Bitmessage</p></body></html> @@ -1793,40 +1847,45 @@ Generowanie adresów 'losowych' jest wybrane domyślnie, jednak determ connectDialog - + Bitmessage Bitmessage - + Bitmessage won't connect to anyone until you let it. Bitmessage nie połączy się, zanim mu na to nie pozwolisz. - + Connect now Połącz teraz - + Let me configure special network settings first Pozwól mi najpierw ustawić specjalne opcje konfiguracji sieci + + + Work offline + Działaj bez sieci + helpDialog - + Help Pomoc - + <a href="https://bitmessage.org/wiki/PyBitmessage_Help">https://bitmessage.org/wiki/PyBitmessage_Help</a> <a href="https://bitmessage.org/wiki/PyBitmessage_Help">https://bitmessage.org/wiki/PyBitmessage_Help</a> - + As Bitmessage is a collaborative project, help can be found online in the Bitmessage Wiki: Ponieważ Bitmessage jest tworzone przez społeczność, możesz uzyskać pomoc w sieci na wiki Bitmessage: @@ -1834,30 +1893,35 @@ Generowanie adresów 'losowych' jest wybrane domyślnie, jednak determ iconGlossaryDialog - + Icon Glossary Opis ikon - + You have no connections with other peers. Nie masz żadnych połączeń z innymi użytkownikami. - + You have made at least one connection to a peer using an outgoing connection but you have not yet received any incoming connections. Your firewall or home router probably isn't configured to forward incoming TCP connections to your computer. Bitmessage will work just fine but it would help the Bitmessage network if you allowed for incoming connections and will help you be a better-connected node. Masz co najmniej jedno połączenie wychodzące z innymi użytkownikami, ale nie masz jeszcze żadnych połączeń przychodzących. Twoja zapora sieciowa lub domowy ruter prawdopodobnie nie są poprawnie skonfigurowane aby przekazywać połączenia przychodzące TCP na Twój komputer. Bitmessage będzie działał dobrze, ale byłoby fajnie, gdybyś pomógł sieci Bitmessage i zezwolił na połączenia przychodzące. You are using TCP port ?. (This can be changed in the settings). - Używasz portu TCP ?. (Możesz go zmienić w ustawieniach.) + - + You do have connections with other peers and your firewall is correctly configured. Masz połączenia z innymi użytkownikami i twoja zapora sieciowa jest skonfigurowana poprawnie. + + + You are using TCP port %1. (This can be changed in the settings). + Btimessage używa portu TCP %1. (Można go zmienić w ustawieniach). + networkstatus @@ -1867,37 +1931,37 @@ Generowanie adresów 'losowych' jest wybrane domyślnie, jednak determ Wszystkich połączeń: - + Since startup: Od startu: - + Processed 0 person-to-person messages. Przetworzono 0 wiadomości zwykłych. - + Processed 0 public keys. Przetworzono 0 kluczy publicznych. - + Processed 0 broadcasts. - Przetworzono 0 wiadomości przekazów. + Przetworzono 0 wiadomości subskrypcji. - + Inventory lookups per second: 0 Zapytań o elementy na sekundę: 0 - + Objects to be synced: Obiektów do zsynchronizowania: - + Stream # Strumień # @@ -1907,112 +1971,112 @@ Generowanie adresów 'losowych' jest wybrane domyślnie, jednak determ - + Since startup on %1 Od startu programu o %1 - + Down: %1/s Total: %2 Pobieranie: %1/s W całości: %2 - + Up: %1/s Total: %2 Wysyłanie: %1/s W całości: %2 - + Total Connections: %1 Wszystkich połączeń: %1 - + Inventory lookups per second: %1 Zapytań o elementy na sekundę: %1 - + Up: 0 kB/s Wysyłanie: 0 kB/s - + Down: 0 kB/s Pobieranie: 0 kB/s - + Network Status Stan sieci - + byte(s) bajtbajtówbajtówbajtów - + Object(s) to be synced: %n Jeden obiekt do zsynchronizowaniaObieków do zsynchronizowania: %nObieków do zsynchronizowania: %nObieków do zsynchronizowania: %n - + Processed %n person-to-person message(s). Przetworzono %n wiadomość zwykłą.Przetworzono %n wiadomości zwykłych.Przetworzono %n wiadomości zwykłych.Przetworzono %n wiadomości zwykłych. - + Processed %n broadcast message(s). - Przetworzono %n wiadomość przekazów.Przetworzono %n wiadomości przekazów.Przetworzono %n wiadomości przekazów.Przetworzono %n wiadomości przekazów. + Przetworzono %n wiadomość subskrypcji.Przetworzono %n wiadomości subskrypcji.Przetworzono %n wiadomości subskrypcji.Przetworzono %n wiadomości subskrypcji. - + Processed %n public key(s). Przetworzono %n klucz publiczny.Przetworzono %n kluczy publicznych.Przetworzono %n kluczy publicznych.Przetworzono %n kluczy publicznych. - + Peer Użytkownik - + IP address or hostname IP lub nazwa hosta - + Rating Ocena - + PyBitmessage tracks the success rate of connection attempts to individual nodes. The rating ranges from -1 to 1 and affects the likelihood of selecting the node in the future PyBitmessage rejestruje pomyślność prób połączeń z indywidualnymi węzłami. Ocena przyjmuje wartości od -1 do 1 i ma wpływ na prawdopodobieństwo wybrania węzła w przyszłości. - + User agent Klient - + Peer's self-reported software Ogłaszana aplikacja kliencka - + TLS TLS - + Connection encryption Szyfrowanie połączenia - + List of streams negotiated between you and the peer Lista strumieni negocjowanych pomiędzy Tobą i użytkownikiem @@ -2071,8 +2135,8 @@ Generowanie adresów 'losowych' jest wybrane domyślnie, jednak determ - Chan passhphrase/name: - Wpisz hasło/nazwę: + Chan passphrase/name: + Nazwa/hasło kanału: @@ -2093,7 +2157,7 @@ Generowanie adresów 'losowych' jest wybrane domyślnie, jednak determ newchandialog - + Successfully created / joined chan %1 Pomyślnie utworzono / dołączono do kanału %1 @@ -2137,52 +2201,52 @@ Generowanie adresów 'losowych' jest wybrane domyślnie, jednak determ regenerateAddressesDialog - + Regenerate Existing Addresses Odtwórz istniejące adresy - + Regenerate existing addresses Odtwórz istniejące adresy - + Passphrase Hasło - + Number of addresses to make based on your passphrase: Liczba adresów do wygenerowanie na podstawie hasła: - + Address version number: Numer wersji adresu: - + Stream number: Numer strumienia: - + 1 1 - + Spend several minutes of extra computing time to make the address(es) 1 or 2 characters shorter Dołóż kilka minut dodatkowych obliczeń aby wygenerować adres(y) krótsze o 1 lub 2 znaki - + You must check (or not check) this box just like you did (or didn't) when you made your addresses the first time. Musisz to zaznaczyć (albo nie zaznaczyć) jeżeli zaznaczyłeś (bądź nie zaznaczyłeś) to podczas tworzenia adresu po raz pierwszy. - + If you have previously made deterministic addresses but lost them due to an accident (like hard drive failure), you can regenerate them here. If you used the random number generator to make your addresses then this form will be of no use to you. Jeżeli poprzednio wygenerowałeś deterministyczne adresy, ale je straciłeś przez przypadek (jak np. awaria dysku), możesz je tutaj odtworzyć. Jeżeli użyłeś generatora liczb losowych do utworzenia adresu, wtedy ten formularz jest dla Ciebie bezużyteczny. From 8a5ec29540e8b4620553af5d2ce4d1fbd4b3f985 Mon Sep 17 00:00:00 2001 From: Peter Surda Date: Thu, 1 Feb 2018 21:24:50 +0100 Subject: [PATCH 367/407] Store object hash as binary in sqlite (inventory) --- src/storage/sqlite.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/storage/sqlite.py b/src/storage/sqlite.py index 7c9e8822..f4107241 100644 --- a/src/storage/sqlite.py +++ b/src/storage/sqlite.py @@ -18,13 +18,13 @@ class SqliteInventory(InventoryStorage): with self.lock: if hash in self._inventory: return True - return bool(sqlQuery('SELECT 1 FROM inventory WHERE hash=?', hash)) + return bool(sqlQuery('SELECT 1 FROM inventory WHERE hash=?', sqlite3.Binary(hash))) def __getitem__(self, hash): with self.lock: if hash in self._inventory: return self._inventory[hash] - rows = sqlQuery('SELECT objecttype, streamnumber, payload, expirestime, tag FROM inventory WHERE hash=?', hash) + rows = sqlQuery('SELECT objecttype, streamnumber, payload, expirestime, tag FROM inventory WHERE hash=?', sqlite3.Binary(hash)) if not rows: raise KeyError(hash) return InventoryItem(*rows[0]) @@ -62,14 +62,14 @@ class SqliteInventory(InventoryStorage): with self.lock: t = int(time.time()) hashes = [x for x, value in self._inventory.items() if value.stream == stream and value.expires > t] - hashes += (payload for payload, in sqlQuery('SELECT hash FROM inventory WHERE streamnumber=? AND expirestime>?', stream, t)) + hashes += (str(payload) for payload, in sqlQuery('SELECT hash FROM inventory WHERE streamnumber=? AND expirestime>?', stream, t)) return hashes def flush(self): with self.lock: # If you use both the inventoryLock and the sqlLock, always use the inventoryLock OUTSIDE of the sqlLock. with SqlBulkExecute() as sql: for objectHash, value in self._inventory.items(): - sql.execute('INSERT INTO inventory VALUES (?, ?, ?, ?, ?, ?)', objectHash, *value) + sql.execute('INSERT INTO inventory VALUES (?, ?, ?, ?, ?, ?)', sqlite3.Binary(objectHash), *value) self._inventory.clear() def clean(self): From 451174b56683288dd134f9e19c43bb41e2ce5d91 Mon Sep 17 00:00:00 2001 From: Peter Surda Date: Thu, 1 Feb 2018 22:31:45 +0100 Subject: [PATCH 368/407] Download tracking fix - don't reset tracking too early - handle inserts when tracking objects --- src/randomtrackingdict.py | 28 +++++++++++++++------------- 1 file changed, 15 insertions(+), 13 deletions(-) diff --git a/src/randomtrackingdict.py b/src/randomtrackingdict.py index 8c3fa6aa..e1616c2c 100644 --- a/src/randomtrackingdict.py +++ b/src/randomtrackingdict.py @@ -22,15 +22,6 @@ class RandomTrackingDict(object): def __getitem__(self, key): return self.dictionary[key][1] - def __setitem__(self, key, value): - with self.lock: - if key in self.dictionary: - self.dictionary[key][1] = value - else: - self.indexDict.append(key) - self.dictionary[key] = [self.len, value] - self.len += 1 - def _swap(self, i1, i2): with self.lock: key1 = self.indexDict[i1] @@ -42,6 +33,16 @@ class RandomTrackingDict(object): # for quick reassignment return i2 + def __setitem__(self, key, value): + with self.lock: + if key in self.dictionary: + self.dictionary[key][1] = value + else: + self.indexDict.append(key) + self.dictionary[key] = [self.len, value] + self._swap(self.len, self.len - self.pendingLen) + self.len += 1 + def __delitem__(self, key): if not key in self.dictionary: raise KeyError @@ -70,12 +71,13 @@ class RandomTrackingDict(object): self.pendingTimeout = pendingTimeout def randomKeys(self, count=1): - if self.lastPoll + self.pendingTimeout < time(): - with self.lock: - self.pendingLen = 0 - if self.len == 0 or self.pendingLen >= self.maxPending: + if self.len == 0 or (self.pendingLen >= self.maxPending and + self.lastPoll + self.pendingTimeout > time(): raise KeyError + # reset if we've requested all with self.lock: + if self.pendingLen == self.len: + self.pendingLen = 0 available = self.len - self.pendingLen if count > available: count = available From dcce7ed4c548ad1def3b03771dee29740a95c2d4 Mon Sep 17 00:00:00 2001 From: Peter Surda Date: Thu, 1 Feb 2018 22:33:28 +0100 Subject: [PATCH 369/407] Typo --- src/randomtrackingdict.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/randomtrackingdict.py b/src/randomtrackingdict.py index e1616c2c..a7961528 100644 --- a/src/randomtrackingdict.py +++ b/src/randomtrackingdict.py @@ -72,7 +72,7 @@ class RandomTrackingDict(object): def randomKeys(self, count=1): if self.len == 0 or (self.pendingLen >= self.maxPending and - self.lastPoll + self.pendingTimeout > time(): + self.lastPoll + self.pendingTimeout > time()): raise KeyError # reset if we've requested all with self.lock: From 8498143783cb98dca28d80a4e3dc4ef997538e4b Mon Sep 17 00:00:00 2001 From: Peter Surda Date: Thu, 1 Feb 2018 22:58:04 +0100 Subject: [PATCH 370/407] Download fixes - check if already have object before requesting --- src/network/downloadthread.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/network/downloadthread.py b/src/network/downloadthread.py index f3beb77c..6da5b838 100644 --- a/src/network/downloadthread.py +++ b/src/network/downloadthread.py @@ -5,6 +5,7 @@ import time import addresses from debug import logger from helper_threading import StoppableThread +from inventory import Inventory from network.connectionpool import BMConnectionPool import protocol from state import missingObjects @@ -53,6 +54,9 @@ class DownloadThread(threading.Thread, StoppableThread): payload = bytearray() payload.extend(addresses.encodeVarint(len(request))) for chunk in request: + if chunk in Inventory(): + del i.objectsNewToMe[chunk] + continue payload.extend(chunk) missingObjects[chunk] = now i.append_write_buf(protocol.CreatePacket('getdata', payload)) From 57c8c7c07c0dd9b96d869b21780a195a3a802001 Mon Sep 17 00:00:00 2001 From: Peter Surda Date: Thu, 1 Feb 2018 23:18:08 +0100 Subject: [PATCH 371/407] Download thread exception handling --- src/network/downloadthread.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/network/downloadthread.py b/src/network/downloadthread.py index 6da5b838..37e36398 100644 --- a/src/network/downloadthread.py +++ b/src/network/downloadthread.py @@ -55,7 +55,10 @@ class DownloadThread(threading.Thread, StoppableThread): payload.extend(addresses.encodeVarint(len(request))) for chunk in request: if chunk in Inventory(): - del i.objectsNewToMe[chunk] + try: + del i.objectsNewToMe[chunk] + except KeyError: + pass continue payload.extend(chunk) missingObjects[chunk] = now From 053f434e0466f503fba8b41d3d7f82aa6450a7be Mon Sep 17 00:00:00 2001 From: Peter Surda Date: Fri, 2 Feb 2018 12:44:43 +0100 Subject: [PATCH 372/407] Download fixes - don't make empty requests - don't make requests if all objects are pending already --- src/network/downloadthread.py | 2 ++ src/randomtrackingdict.py | 5 +++-- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/src/network/downloadthread.py b/src/network/downloadthread.py index 37e36398..88f7c12e 100644 --- a/src/network/downloadthread.py +++ b/src/network/downloadthread.py @@ -62,6 +62,8 @@ class DownloadThread(threading.Thread, StoppableThread): continue payload.extend(chunk) missingObjects[chunk] = now + if not payload: + continue i.append_write_buf(protocol.CreatePacket('getdata', payload)) logger.debug("%s:%i Requesting %i objects", i.destination.host, i.destination.port, len(request)) requested += len(request) diff --git a/src/randomtrackingdict.py b/src/randomtrackingdict.py index a7961528..83d35cdf 100644 --- a/src/randomtrackingdict.py +++ b/src/randomtrackingdict.py @@ -71,8 +71,9 @@ class RandomTrackingDict(object): self.pendingTimeout = pendingTimeout def randomKeys(self, count=1): - if self.len == 0 or (self.pendingLen >= self.maxPending and - self.lastPoll + self.pendingTimeout > time()): + if self.len == 0 or ((self.pendingLen >= self.maxPending or + self.pendingLen == self.len) and self.lastPoll + + self.pendingTimeout > time()): raise KeyError # reset if we've requested all with self.lock: From 96b8cff0d13a44898322753a1c7d5704448c3186 Mon Sep 17 00:00:00 2001 From: Peter Surda Date: Fri, 2 Feb 2018 14:33:29 +0100 Subject: [PATCH 373/407] Inventory checking performance optimisation - caching of whether an object exists in inventory was somehow removed since storage refactoring (or it never worked). Now existence checking is cached in the sqlite storage backend --- src/storage/sqlite.py | 20 ++++++++++---------- src/storage/storage.py | 3 --- 2 files changed, 10 insertions(+), 13 deletions(-) diff --git a/src/storage/sqlite.py b/src/storage/sqlite.py index f4107241..438cbdcb 100644 --- a/src/storage/sqlite.py +++ b/src/storage/sqlite.py @@ -11,14 +11,18 @@ class SqliteInventory(InventoryStorage): def __init__(self): super(self.__class__, self).__init__() self._inventory = {} #of objects (like msg payloads and pubkey payloads) Does not include protocol headers (the first 24 bytes of each packet). - self._streams = collections.defaultdict(set) # key = streamNumer, value = a set which holds the inventory object hashes that we are aware of. This is used whenever we receive an inv message from a peer to check to see what items are new to us. We don't delete things out of it; instead, the singleCleaner thread clears and refills it every couple hours. + self._objects = {} # cache for existing objects, used for quick lookups if we have an object. This is used for example whenever we receive an inv message from a peer to check to see what items are new to us. We don't delete things out of it; instead, the singleCleaner thread clears and refills it. self.lock = RLock() # Guarantees that two receiveDataThreads don't receive and process the same message concurrently (probably sent by a malicious individual) def __contains__(self, hash): with self.lock: - if hash in self._inventory: + if hash in self._objects: return True - return bool(sqlQuery('SELECT 1 FROM inventory WHERE hash=?', sqlite3.Binary(hash))) + rows = sqlQuery('SELECT streamnumber FROM inventory WHERE hash=?', sqlite3.Binary(hash)) + if not rows: + return False + self._objects[hash] = rows[0][0] + return True def __getitem__(self, hash): with self.lock: @@ -33,7 +37,7 @@ class SqliteInventory(InventoryStorage): with self.lock: value = InventoryItem(*value) self._inventory[hash] = value - self._streams[value.stream].add(hash) + self._objects[hash] = value.stream def __delitem__(self, hash): raise NotImplementedError @@ -54,10 +58,6 @@ class SqliteInventory(InventoryStorage): values += (InventoryItem(*value) for value in sqlQuery('SELECT objecttype, streamnumber, payload, expirestime, tag FROM inventory WHERE objecttype=? AND tag=?', objectType, sqlite3.Binary(tag))) return values - def hashes_by_stream(self, stream): - with self.lock: - return self._streams[stream] - def unexpired_hashes_by_stream(self, stream): with self.lock: t = int(time.time()) @@ -75,7 +75,7 @@ class SqliteInventory(InventoryStorage): def clean(self): with self.lock: sqlExecute('DELETE FROM inventory WHERE expirestime Date: Fri, 2 Feb 2018 18:31:07 +0200 Subject: [PATCH 374/407] Set fixed size for simple dialogs --- src/bitmessageqt/dialogs.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/bitmessageqt/dialogs.py b/src/bitmessageqt/dialogs.py index 80bffed4..cb82f348 100644 --- a/src/bitmessageqt/dialogs.py +++ b/src/bitmessageqt/dialogs.py @@ -44,7 +44,7 @@ class AboutDialog(QtGui.QDialog, RetranslateMixin): except AttributeError: pass - QtGui.QWidget.resize(self, QtGui.QWidget.sizeHint(self)) + self.setFixedSize(QtGui.QWidget.sizeHint(self)) class IconGlossaryDialog(QtGui.QDialog, RetranslateMixin): @@ -59,18 +59,18 @@ class IconGlossaryDialog(QtGui.QDialog, RetranslateMixin): "iconGlossaryDialog", "You are using TCP port %1. (This can be changed in the settings)." ).arg(config.getint('bitmessagesettings', 'port'))) - QtGui.QWidget.resize(self, QtGui.QWidget.sizeHint(self)) + self.setFixedSize(QtGui.QWidget.sizeHint(self)) class HelpDialog(QtGui.QDialog, RetranslateMixin): def __init__(self, parent=None): super(HelpDialog, self).__init__(parent) widgets.load('help.ui', self) - QtGui.QWidget.resize(self, QtGui.QWidget.sizeHint(self)) + self.setFixedSize(QtGui.QWidget.sizeHint(self)) class ConnectDialog(QtGui.QDialog, RetranslateMixin): def __init__(self, parent=None): super(ConnectDialog, self).__init__(parent) widgets.load('connect.ui', self) - QtGui.QWidget.resize(self, QtGui.QWidget.sizeHint(self)) + self.setFixedSize(QtGui.QWidget.sizeHint(self)) From ec30472dd8dd0e7eab9db68a5ac84fd7f2d350cc Mon Sep 17 00:00:00 2001 From: Dmitri Bogomolov <4glitch@gmail.com> Date: Fri, 2 Feb 2018 18:31:53 +0200 Subject: [PATCH 375/407] AboutDialog formatting fix (hope) --- src/bitmessageqt/about.ui | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/src/bitmessageqt/about.ui b/src/bitmessageqt/about.ui index 099875c0..8ec7159c 100644 --- a/src/bitmessageqt/about.ui +++ b/src/bitmessageqt/about.ui @@ -22,9 +22,6 @@ :/newPrefix/images/can-icon-24px.png - - true - Qt::AlignCenter @@ -42,7 +39,7 @@ <html><head/><body><p><a href="https://github.com/Bitmessage/PyBitmessage/tree/:branch:"><span style="text-decoration:none; color:#0000ff;">PyBitmessage :version:</span></a></p></body></html> - Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + Qt::AlignCenter From d03d4a374e46720d4a35014b76d0aedc45eeb8cd Mon Sep 17 00:00:00 2001 From: PyBitmessage Translations Date: Fri, 2 Feb 2018 18:29:41 +0100 Subject: [PATCH 376/407] Auto-updated language de from transifex --- src/translations/bitmessage_de.qm | Bin 98493 -> 96660 bytes src/translations/bitmessage_de.ts | 668 ++++++++++++++++-------------- 2 files changed, 361 insertions(+), 307 deletions(-) diff --git a/src/translations/bitmessage_de.qm b/src/translations/bitmessage_de.qm index c04e1950848d63fcd660fbaab722484281145989..9fa26fae11ce88ebd486bcc7bd5f502ddad73991 100644 GIT binary patch delta 3948 zcmZWsd0dTY8@`_NzVF%I_nf1}RwH37rIJXCQT9k3L$r`;QnV>4OQC~|Ju#=0BwLmY zp)}bBVP-JY5ShXZBV#@)OJw;B-(BW6|9rpmtGn~O@AKT(bKTdy^!-wKR7M`8}Y`Td>@p)k5J7DGHofI}dAH+>vtS%^h4H1>H$3Y4|58845nDj zoRVl?aRnQgMgXVmW$q@$fN=yH7VJXj>6zY8LkA3%v9KUQ{>F!eLkpyBXHoCzfc8&W zyc^v-w<}vc%>k&aVcAEB`p-AAEeDChukNvvg&dIGj(tC%2#`JaWPKvp&yq8+c^P}N zn;b^(W*=oEfL^y{!YOjRcFZH)V#!34mj`Y)y>` znB6nky4L4;^JN*cn}BoUWZS~(fj;wOdxGixnWGJ|t6_w2L%6JF*5{<|N3vUs&yz@o z%IdUVfpw{n{mCZ*FE4TYB4;qm!JM>C0GocA(_AY7`cC9@UsK3l1#)I34}h#p&N|a*owU2w(>1EYbNk_qjm{X%sSpD>p=M4Hgl=4GXOWF8Oj} z6M3Mlh>Lnp)K5y_QXHrjbp4&%TvZC(IL>W;9|CM`H7!V^{Qs#x_vRR>c*RU!*JV9e z=cBwXl(JxA55B|xAQEL2Kct)r&6g|r5qgU86AymORTANh7Czi8i*D%0M;>yZI#J2T zip1o{&V2H0V#stqKmTuvdHp?p>8qQ-@dkd?APVi0ll(XNlsy9z`EN5B3>ZF^KVUzqQC6Qo3w!*5zUZjncLWVmz z+WsFQ*Ubi~dm`jDCxJQN7q+Y-$65V^;?)8H$`FoDCdaYUgcCFA{jIZvOI6MUJYA?< zYoI)z5H39S8V}h2F0^n&QF4^_F;DEE0h1oc;{3xHhfzmN3RK98+#WZA|Jm32vSWItu;hidAS-yOqF$m~kD?iwmZXWnR zUb@~4c;+s@Xqcu2MyljD_M1{VdCBkkQ!FR%k~hi8!Tu73=>}qGsY>CZCI&1l74Bd4 z1~OJEd}}L#Z(0=o8x{l4e^EsCkERNkr-%%nOgFosNZ;m3=l-l%;nNvxYG1{A=ih)Y zUnn+opsX+rR&01kKzzSZ7_zGi0DmtkjsC|>tG4OE3H zUFFZg0$(UypEptAdTUVnc}%8-PRgLuS%6KPa{ed>ASGJ4X4e$p;RxkMTT*?UgVK0b z3ta1|JQX+$%*<4I>ZAuT(X70@?*i2iJLS&?E1slJw*_N>D9*`vI)e9o4$A0rwg2h^)CE)25qZR5uQBS#1|QN`q&h01fcpQmQR--Xn5&vxmCa;~iDFTk6t)4^&9v)yG9DK-+TFXDjJOR!-{kODIjZ?^XY@f<%`f zs_zFsq)ORM-Iznb#yl69Z!u|Pg{Vr2q>H4BYVI1a+ErB7CmZPI)uLwUD7yK5u~S|= zX`n*v_3|`e`(AXErvcY5iEdx^q!B7t^a!N&peLfI8L3((iF)rfKto)cWdk*${saL$ z*j1eHu?TF^V{!UI(u&nXacyck*#+W;Gj#;~ikMeDg6c$|n6EMnq=Iuq+#~)DoXHWt z{hLN9^N!-hCDFhI3-RI~gnaKA@z!QNm}@t&Hl>Nu>7!UTCYu265gSZ{deVCMDk|T7=ITmm9%EVk?GR01QPLcg>;f7(Hzm(W?9fa>CC2Kz?wwKQ28hg z7!@o15~BxNB;lxwY?S<7elD(2%uB6x*4``HsmQieDG?sBBvidoi z&!?ZKQLI?gYcn}W?xe9d?gw61XdKs^B<*~!acenEZF-013sVn3Z_tbk8cP$llVm!{y?O5oQ+nwra0IaQXL`pj^OvA5>UEdnC5(Y%csN+F`L>MVuMF-@!N zM4_}et~HPANrQ>uwYJA_J&8=NwQWxaB`LLb`Yj~FzS=P@)P`LyX;ZI|qw!a@D_AkG z@i%SeQ+n^sCGF;;MF3l(&C8=at|`}UpGyBrEPJlqVNJ{^%CuMVDNU7C+RA>NDNXUo z_Rjlcn>{~S!^g$iT0MyD;6j?})L~!@BW>MDdj)x+#ZGgNajhVR_4`^!n=}-qU(+tS&y& zjWm_0OSB^fI(F189c_g`W_??iS$70jK37+0RZ9%a((S+c0nF{PuGmWp{CAb^Sl0ro zbb8&%jx54_Ml976e?ouPY|K|Ld=kvdV_;G8ri)0dvr%odEU z9j6(Gwd-VjwYQ?p{> z((T(OD@!f*&lHT;i*1eH9$MCFO9Kv1FlW56=)z4FS-SX#J*>3v#d5Lq&J9l;^EQ4~ z-^FAuAqpoq5pCFJBH5_-u_z6%FS0Rq|LJYc9}?>(W3rs_&dff?UCYc_QBIJS`B@eb zEGIb9f2#vv|Ic48v^EJ*#)?^%W+Um$)?3FB^x19KNXq%BW%evR$6m**W#@`={B+Dl zI#*=v*J?HH5?QidW7R67wYC6Du3e+3`hI#?MHNf~U0c!^!${Eb_5b$9)TE7J(hl6F!hU}6j zkd_Tu{Zt^m2C~O~fVq8>WV&*2si(o(C4pNm16FqfSMmzj*%sV}aMD|%ribys#-rf& zl!GO5;0`YVONoGdmy`@tKrt}_%;E@D#1T3$4BD-A!0_vEjCcw>%Yx&+)nJ`Ig%- z>y<|i##ON($4X%B6E<*4AJW&38E(+|u)ZudYAYC8vv@1Cz=D-5`3)&(`IZC)=WOzr7L`Z)Ce7{4|J@hV+n=0`>Nk}h-j{o z+`hhfuw+F>BXHhLvMKgH(0z}jVhZg~{XtT#t)$4;O1_CDM~elk0e4Um-hoci>11cD0Oc=rOx{s2$4Xk zegl!~Y?Rca?>4Z`g48>V%D8rc)bHv(Aoz?lKp_2occs0}d6c?=(*6cLSgJKvIxvQ6 z|8iUDuuK_nEKi#JhEfp4OLKkh0UoZ>br-9F+l!>@-b4f2j!Da+KO%>Vq&31>a+sH% zx6ylUPidXP2>4r?B;&Qxi;E3Fq`$PGbQp1hmp+Ol1hzCt8)xMK7a@Iln8<8hI#H%; zR|uxxC)33&1&i{Mwb&g6rfetce~21PWRWb)K)HU(%Z7eKWK3Nxi+3)j2*=2bpZZXL zxG78J2~xS2EUn9IKt5EKok2*o-XY6*OB{H-SC;?mI&kuoY;kW&b-`}g>J3y)eRjw` zS;?sWBdoGLUCHxfk7Rqiwo`dj$*MQF0LS0TYIcW!1;}Nm9(N^2{v|t`>j{khMRvpK zG_BiYw@y4IvQ^3M=C}jzlYrV#bnr4&s8qrs8OBaKAS*>%_F&* zB-+2NA6K`G%6(EQ_h^W9G_`+M?zxnpnH9;s2zR7T_b<8QbW)VCS3dI5WUw9`<>R&! z`+IMb$7Yp*Ikc0fPcs2WC35q1e>(q0KBI`rGd5YicqZjG!AZVh_>W+je)96$7lB2~ zm&FKQ~ni3<;HA+wDYUHb#DDBIS0<9(kjj z3{~YToQepkf|rUuYC@tzD@9OTXJFM##i;r^YA~A>6N}~ne{NJ5d!$fj+@@H#DU=2n zQ!ES3|Nv*gvF&a?$D(=Y1Ky{Jg zpVtF`6FS8M!!_!B4;3#wYk`|y%D(d7z+zrom3@C}1lDa+jtiMUA37+bYKsA{t;(DT zA7I`j<9r#yBvgpl|_d4A_v>isvAUt8U& z^Q9|qEizHL`6zF#qxZPw%KLYsfoVOIPhOP)W6m|p*VZcK^97l}i`^=hpT>c;`B~Lw z2jx1ZP}S+{->9yis{BiG=}u6i>N6z)n7K{W*C7Z{hpC2Nh^9{Yk!mE~1Lq#7jD^IV z@!l$nfgDNOqRRjC9a#H%)$(EFK)*{W(LqMT(W)wb(i1QYP?g$|v2MSj+Hf$4=JzPo z#)V%2cYacB?)RSRxk$Bd3z5^jO!bYw8<_tj)i-Ym;vVU$iw#t!<|eLx0p z|EjJGc}1P@p1L~n9W|OT^%0&L)%GOy$vO(m(^q|FK9zCBO!cKjM9QpJ>hGug2z=M5 zeqzg``9Jy<&qh@ei6-!>Tq8wtgjY*111mc7>igLg`6XVHA3>4d9FGRZ_T~NLdBCl;eBc;Qx@xWALnhIB^v8UtGqGPanKuky3j7h+Bw4VUH`KhN zhSkWAeQ(_b_Tf`LVGfbW^9O#}?1dy}@I@zXQ3UJx(yL+AP-6HEs$SHXKIAL-e*#}l z<3D*zSFScL{JHrlz}G+X=YF67_7?It*BQVvD0PoxUW2eqwf+zo2 zPmRYgP4Kf<$<~#^0IQcB+Mk5*1Jt3G?H4A^OQmtyO-OteL&Ky>Fs>~F8Wsqtdj}Cx zE<(nzjpXnkA^W>vYACOT+$ctks6klvc@b5EC=`bhS5n=C;`rgf?`?%$vz}5FwGs9^ zksi+^VSj&0@$nwQetVT1@)xX!&BW@L*My_YLM`}MlVq{`gcEBA0)??c-Ge-8BwK__ z(+xCk>xIj@M!GK)3-!%kIz_YW zcBxsu7MdiJf32-I(ADhu_$JBvXKH^L+mUWC#oBl7S{l!Xb-tc%)N)Vj{7o~!CS1}5 zoO7q!?|WTv8@ea-oT?jfkPyq;q#IpM2w$78n_N{3<}^tcTUtQ#KWw=!@eM7M1?fyH z1Bp~SbeUd+fV+z>e=sTP{H<=~t$o1aL|wUiJ&-d+xBHuSU<0n|Du<8*l1Sa*_GQ%Z z9_fx|Hv*?d>rT6pJfPBDF&U`UcG3OVpXz;GAKj}JG*e=q(}c2w6W!qh!YqS?(=yyxmJe&sNTd5eOhRV;9Gcbo|$vM>#Akc4=|Axo@q^pN^MZ3|+o zgJ@`}^h}~HIkdq93lflwYa`dT$(wF*#jIbu5{`uB`cz%vm5`n3AYHbgG=jn|5J;|IVb!6A2yf| z!YKlKtV!YmT~G0Stx}v9Du`o7%Wcbonb6XUmf~qk_Fs@>5RzuZi(`CTZFj?%ytVzT zi8RchKOcJjPkk{h{~N71+GVjPLLB?bIdb`ULMF?YIm2klHYVzgc0}}XiHTW8i$$Me z(VNWK`nZ|&F)lvM=%vs23)8GzeVpE8%+Z@oMj!p)l(aOxc}8{<%=WKMPuD|(KFaVjCNULmn>0?aq$)d zT{G=()!oaJIF=xW?)DW=@0!aJszZ0rc3~2+ruKUEsuSCpt-YMR6qC+7RQEo0h__|7 zV2{M!(;cdxTnN>%p`!QgcB15>Q+4$1lU*gOg{{L7=50$-Fc-cP1!P7BauIBst6<|i z{^JA1=?_2pw@6Q_ndYb7^etS>U*5*moA9w$PY$7wfK)rS$g=&SW?m93vvn9jR;DPK zTHYzaoRJ%BOIEV34$aScf4?3*z3Hd-p^Zv*Mao)~(8hl%_UfmqGVjvlrP3cai qz=#=QMqwMVTdrDc{Ilv%=pdHQ*fra!FqXi)!~t*a9E@Paz5We!3}Yw& diff --git a/src/translations/bitmessage_de.ts b/src/translations/bitmessage_de.ts index 1dd83139..4e4db13e 100644 --- a/src/translations/bitmessage_de.ts +++ b/src/translations/bitmessage_de.ts @@ -2,17 +2,17 @@ AddAddressDialog - + Add new entry Neuen Eintrag erstellen - + Label Name oder Bezeichnung - + Address Adresse @@ -20,70 +20,99 @@ EmailGatewayDialog - + Email gateway E-Mail Schnittstelle - + Register on email gateway An E-Mail Schnittstelle registrieren - + Account status at email gateway Statusanfrage der E-Mail Schnittstelle - + Change account settings at email gateway Einstellungen der E-Mail Schnittstelle ändern - + Unregister from email gateway Von der E-Mail Schnittstelle abmelden - + Email gateway allows you to communicate with email users. Currently, only the Mailchuck email gateway (@mailchuck.com) is available. Die E-Mail Schnittstelle ermöglicht es, mit anderen E-Mail Nutzern zu kommunizieren. Zur Zeit ist nur die Mailchuck-E-Mail Schnittstelle (@mailchuck.com) verfügbar. - + Desired email address (including @mailchuck.com): Gewünschte E-Mailaddresse (inkl. @mailchuck.com): + + + @mailchuck.com + + + + + Registration failed: + Registrierung fehlgeschlagen: + + + + The requested email address is not available, please try a new one. + + + + + Sending email gateway registration request + Der Registrierungsantrag für die E-Mail Schnittstelle wird versandt. + + + + Sending email gateway unregistration request + E-Mail Schnittestellen-Abmeldeantrag wird versandt + + + + Sending email gateway status request + E-Mail Schnittestellen Statusantrag wird versandt + EmailGatewayRegistrationDialog - + Registration failed: - Registrierung fehlgeschlagen: + - + The requested email address is not available, please try a new one. Fill out the new desired email address (including @mailchuck.com) below: - Die gewünschte E-Mailaddresse ist nicht verfügbar, bitte probieren Sie eine neue. Die gewünschte E-Mailaddresse (inkl. @mailchuck.com) unten ausfüllen: + Email gateway registration - E-Mail Schnittstellen Registrierung + Email gateway allows you to communicate with email users. Currently, only the Mailchuck email gateway (@mailchuck.com) is available. Please type the desired email address (including @mailchuck.com) below: - Die E-Mail Schnittstelle ermöglicht es, mit anderen E-Mail-Nutzern zu kommunizieren. Zur Zeit ist nur die Mailchuck-E-Mail-Schnittstelle verfügbar (@mailchuck.com). -Bitte geben Sie die gewünschte E-Mail-Adresse (inkl. @mailchuck.com) unten ein: + Mailchuck - + # You can use this to configure your email gateway account # Uncomment the setting you want to use # Here are the options: @@ -174,52 +203,52 @@ Bitte geben Sie die gewünschte E-Mail-Adresse (inkl. @mailchuck.com) unten ein: MainWindow - + Reply to sender Dem Absender antworten - + Reply to channel Antworten in den Chan - + Add sender to your Address Book Absender zum Adressbuch hinzufügen - + Add sender to your Blacklist Absender in die Blacklist eintragen - + Move to Trash In den Papierkorb verschieben - + Undelete Wiederherstellen - + View HTML code as formatted text HTML als formatierten Text anzeigen - + Save message as... Nachricht speichern unter... - + Mark Unread Als ungelesen markieren - + New Neu @@ -244,12 +273,12 @@ Bitte geben Sie die gewünschte E-Mail-Adresse (inkl. @mailchuck.com) unten ein: Adresse in die Zwischenablage kopieren - + Special address behavior... Spezielles Verhalten der Adresse... - + Email gateway E-Mail Schnittstelle @@ -259,37 +288,37 @@ Bitte geben Sie die gewünschte E-Mail-Adresse (inkl. @mailchuck.com) unten ein: Löschen - + Send message to this address Nachricht an diese Adresse senden - + Subscribe to this address Diese Adresse abonnieren - + Add New Address Neue Adresse hinzufügen - + Copy destination address to clipboard Zieladresse in die Zwischenablage kopieren - + Force send Senden erzwingen - + One of your addresses, %1, is an old version 1 address. Version 1 addresses are no longer supported. May we delete it now? Eine Ihrer Adressen, %1, ist eine alte Adresse der Version 1 und wird nicht mehr unterstützt. Soll sie jetzt gelöscht werden? - + Waiting for their encryption key. Will request it again soon. Warte auf den Verschlüsselungscode. Wird bald erneut angefordert. @@ -299,17 +328,17 @@ Bitte geben Sie die gewünschte E-Mail-Adresse (inkl. @mailchuck.com) unten ein: - + Queued. In Warteschlange. - + Message sent. Waiting for acknowledgement. Sent at %1 Nachricht gesendet. Warte auf Bestätigung. Zeitpunkt der Sendung: %1 - + Message sent. Sent at %1 Nachricht gesendet. Zeitpunkt der Sendung: %1 @@ -319,77 +348,77 @@ Bitte geben Sie die gewünschte E-Mail-Adresse (inkl. @mailchuck.com) unten ein: - + Acknowledgement of the message received %1 Bestätigung der Nachricht erhalten %1 - + Broadcast queued. Rundruf in Warteschlange. - + Broadcast on %1 Rundruf um %1 - + Problem: The work demanded by the recipient is more difficult than you are willing to do. %1 Problem: Die vom Empfänger geforderte Arbeit ist schwerer als Sie bereit sind, zu berechnen. %1 - + Problem: The recipient's encryption key is no good. Could not encrypt message. %1 Problem: Der Verschlüsselungscode des Empfängers ist nicht in Ordnung. Nachricht konnte nicht verschlüsselt werden. %1 - + Forced difficulty override. Send should start soon. Schwierigkeitslimit überschrieben. Senden sollte bald beginnen. - + Unknown status: %1 %2 Unbekannter Status: %1 %2 - + Not Connected Nicht verbunden - + Show Bitmessage Bitmessage anzeigen - + Send Senden - + Subscribe Abonnieren - + Channel Chan - + Quit Beenden - + You may manage your keys by editing the keys.dat file stored in the same directory as this program. It is important that you back up this file. Sie können Ihre Schlüssel verwalten, indem Sie die keys.dat bearbeiten, die im gleichen Ordner wie das Programm liegt. Es ist empfehlenswert, vorher ein Backup dieser Datei anzulegen. - + You may manage your keys by editing the keys.dat file stored in %1 It is important that you back up this file. @@ -398,17 +427,17 @@ It is important that you back up this file. Es ist empfehlenswert, vorher ein Backup dieser Datei anzulegen. - + Open keys.dat? Die keys.dat öffnen? - + You may manage your keys by editing the keys.dat file stored in the same directory as this program. It is important that you back up this file. Would you like to open the file now? (Be sure to close Bitmessage before making any changes.) Sie können Ihre Schlüssel verwalten, indem Sie die keys.dat bearbeiten, die im gleichen Ordner wie das Programm liegt. Es ist empfehlenswert, vorher ein Backup dieser Datei anzulegen. Möchten Sie die Datei jetzt öffnen? (Stellen Sie sicher, dass Sie Bitmessage beendet haben, bevor Sie etwas ändern.) - + You may manage your keys by editing the keys.dat file stored in %1 It is important that you back up this file. Would you like to open the file now? (Be sure to close Bitmessage before making any changes.) @@ -418,12 +447,12 @@ Es ist empfehlenswert, vorher ein Backup dieser Datei anzulegen. Möchten Sie di (Stellen Sie sicher, dass Sie Bitmessage beendet haben, bevor Sie etwas ändern.) - + Delete trash? Papierkorb leeren? - + Are you sure you want to delete all trashed messages? Sind Sie sicher, dass Sie alle Nachrichten im Papierkorb löschen möchten? @@ -438,17 +467,17 @@ Es ist empfehlenswert, vorher ein Backup dieser Datei anzulegen. Möchten Sie di Sie müssen Ihr Passwort eingeben. Wenn Sie keins haben, ist dies das falsche Formular für Sie. - + Bad address version number Falsche Addressenversionsnummer - + Your address version number must be a number: either 3 or 4. Die Addressenversionsnummer muss eine Zahl sein, entweder 3 oder 4. - + Your address version number must be either 3 or 4. Die Addressenversionnsnummer muss entweder 3 oder 4 sein. @@ -518,22 +547,22 @@ Es ist empfehlenswert, vorher ein Backup dieser Datei anzulegen. Möchten Sie di - + Connection lost Verbindung verloren - + Connected Verbunden - + Message trashed Nachricht in den Papierkorb verschoben - + The TTL, or Time-To-Live is the length of time that the network will hold the message. The recipient must get it during this time. If your Bitmessage client does not hear an acknowledgement, it will resend the message automatically. The longer the Time-To-Live, the @@ -541,17 +570,17 @@ Es ist empfehlenswert, vorher ein Backup dieser Datei anzulegen. Möchten Sie di Die Haltbarkeit, oder Time-To-Live, ist die Dauer, für die das Netzwerk die Nachricht speichern wird. Der Empfänger muss sie während dieser Zeit empfangen. Wenn Ihr Bitmessage-Client keine Empfangsbestätigung erhält, wird die Nachricht automatisch erneut verschickt. Je länger die Time-To-Live, desto mehr Arbeit muss Ihr Rechner verrichten, um die Nachricht zu senden. Eine Time-To-Live von vier oder fünf Tagen ist meist ausreichend. - + Message too long Narchricht zu lang - + The message that you are trying to send is too long by %1 bytes. (The maximum is 261644 bytes). Please cut it down before sending. Die Nachricht, die Sie zu senden versuchen, ist %1 Byte zu lang. (Maximum 261.644 Bytes). Bitte verringern Sie ihre Größe vor dem Senden. - + Error: Your account wasn't registered at an email gateway. Sending registration now as %1, please wait for the registration to be processed before retrying sending. Fehler: Ihr Konto war an keiner E-Mail Schnittstelle registriert. Registrierung als %1 wird versandt, bitte vor einem erneutem Sendeversuch auf die Registrierungsverarbeitung warten. @@ -596,57 +625,57 @@ Es ist empfehlenswert, vorher ein Backup dieser Datei anzulegen. Möchten Sie di - + Error: You must specify a From address. If you don't have one, go to the 'Your Identities' tab. Fehler: Sie müssen eine Absenderadresse auswählen. Sollten Sie keine haben, wechseln Sie zum Reiter "Ihre Identitäten". - + Address version number Adressversion - + Concerning the address %1, Bitmessage cannot understand address version numbers of %2. Perhaps upgrade Bitmessage to the latest version. Aufgrund der Adresse %1 kann Bitmessage Adressen mit der Version %2 nicht verarbeiten. Möglicherweise müssen Sie Bitmessage auf die aktuelle Version aktualisieren. - + Stream number Datenstrom Nummer - + Concerning the address %1, Bitmessage cannot handle stream numbers of %2. Perhaps upgrade Bitmessage to the latest version. Aufgrund der Adresse %1 kann Bitmessage den Datenstrom mit der Version %2 nicht verarbeiten. Möglicherweise müssen Sie Bitmessage auf die aktuelle Version aktualisieren. - + Warning: You are currently not connected. Bitmessage will do the work necessary to send the message but it won't send until you connect. Warnung: Sie sind aktuell nicht verbunden. Bitmessage wird die nötige Arbeit zum versenden verrichten, aber erst senden, wenn Sie verbunden sind. - + Message queued. Nachricht befindet sich in der Warteschleife. - + Your 'To' field is empty. Ihr "Empfänger"-Feld ist leer. - + Right click one or more entries in your address book and select 'Send message to this address'. Klicken Sie mit rechts auf einen oder mehrere Einträge aus Ihrem Adressbuch und wählen Sie "Nachricht an diese Adresse senden". - + Fetched address from namecoin identity. Adresse aus Namecoin Identität geholt. - + New Message Neue Nachricht @@ -656,9 +685,9 @@ Es ist empfehlenswert, vorher ein Backup dieser Datei anzulegen. Möchten Sie di - + Sending email gateway registration request - Der Registrierungsantrag für die E-Mail Schnittstelle wird versandt. + @@ -671,142 +700,142 @@ Es ist empfehlenswert, vorher ein Backup dieser Datei anzulegen. Möchten Sie di Die von Ihnen eingegebene Adresse ist ungültig, sie wird ignoriert. - + Error: You cannot add the same address to your address book twice. Try renaming the existing one if you want. Fehler: Sie können eine Adresse nicht doppelt im Adressbuch speichern. Sie können jedoch die bereits eingetragene umbenennen. - + Error: You cannot add the same address to your subscriptions twice. Perhaps rename the existing one if you want. Fehler: Dieselbe Adresse kann nicht doppelt in die Abonnements eingetragen werden. Sie können jedoch die bereits eingetragene umbenennen. - + Restart Neustart - + You must restart Bitmessage for the port number change to take effect. Sie müssen Bitmessage neu starten, um den geänderten Port zu verwenden. - + Bitmessage will use your proxy from now on but you may want to manually restart Bitmessage now to close existing connections (if any). Bitmessage wird ab sofort den Proxy-Server verwenden, aber eventuell möchten Sie Bitmessage neu starten um bereits bestehende Verbindungen zu schließen. - + Number needed Zahl erforderlich - + Your maximum download and upload rate must be numbers. Ignoring what you typed. Ihre maximale Herungerlade- und Hochladegeschwindigkeit müssen Zahlen sein. Die eingetragenen Werte werden ignoriert. - + Will not resend ever Wird nie wiederversendet - + Note that the time limit you entered is less than the amount of time Bitmessage waits for the first resend attempt therefore your messages will never be resent. Bitte beachten Sie, dass der eingetratene Dauer kürzer ist als die, die Bitmessage auf das erste Wiederversenden wartet. Deswegen werden Ihre Nachrichten nie wiederversendet. - + Sending email gateway unregistration request - E-Mail Schnittestellen-Abmeldeantrag wird versandt + - + Sending email gateway status request - E-Mail Schnittestellen Statusantrag wird versandt + - + Passphrase mismatch Kennwort stimmt nicht überein - + The passphrase you entered twice doesn't match. Try again. Die von Ihnen eingegebenen Kennwörter sind nicht identisch. Bitte neu versuchen. - + Choose a passphrase Wählen Sie ein Kennwort - + You really do need a passphrase. - Sie benötigen wirklich ein Kennwort. + Sie benötigen unbedingt ein Kennwort. - + Address is gone Adresse ist verloren - + Bitmessage cannot find your address %1. Perhaps you removed it? Bitmessage kann Ihre Adresse %1 nicht finden. Haben Sie sie gelöscht? - + Address disabled Adresse deaktiviert - + Error: The address from which you are trying to send is disabled. You'll have to enable it on the 'Your Identities' tab before using it. Fehler: Die Adresse von der Sie versuchen zu senden ist deaktiviert. Sie müssen sie unter dem Reiter "Ihre Identitäten" aktivieren bevor Sie fortfahren. - + Entry added to the Address Book. Edit the label to your liking. - Eintrag dem Adressbuch hinzugefügt. Editieren Sie den Eintrag nach Belieben. + - + Entry added to the blacklist. Edit the label to your liking. Eintrag in die Blacklist hinzugefügt. Die Beschriftung können Sie ändern. - + Error: You cannot add the same address to your blacklist twice. Try renaming the existing one if you want. Fehler: Dieselbe Addresse kann nicht doppelt in die Blacklist eingetragen werden. Sie können jedoch die bereits eingetragene umbenennen. - + Moved items to trash. Objekt(e) in den Papierkorb verschoben. - + Undeleted item. Nachricht wiederhergestellt. - + Save As... Speichern unter... - + Write error. Fehler beim Speichern. - + No addresses selected. Keine Adresse ausgewählt. - + If you delete the subscription, messages that you already received will become inaccessible. Maybe you can consider disabling the subscription instead. Disabled subscriptions will not receive new messages, but you can still view messages you already received. Are you sure you want to delete the subscription? @@ -815,7 +844,7 @@ Are you sure you want to delete the subscription? Sind Sie sicher, dass Sie das Abonnement löschen möchten? - + If you delete the channel, messages that you already received will become inaccessible. Maybe you can consider disabling the channel instead. Disabled channels will not receive new messages, but you can still view messages you already received. Are you sure you want to delete the channel? @@ -824,277 +853,277 @@ Are you sure you want to delete the channel? Sind Sie sicher, dass Sie das Chan löschen möchten? - + Do you really want to remove this avatar? Wollen Sie diesen Avatar wirklich entfernen? - + You have already set an avatar for this address. Do you really want to overwrite it? Sie haben bereits einen Avatar für diese Adresse gewählt. Wollen Sie ihn wirklich überschreiben? - + Start-on-login not yet supported on your OS. Mit Betriebssystem starten, noch nicht von Ihrem Betriebssystem unterstützt - + Minimize-to-tray not yet supported on your OS. Ins System Tray minimieren von Ihrem Betriebssytem noch nicht unterstützt. - + Tray notifications not yet supported on your OS. Trach-Benachrichtigungen von Ihrem Betriebssystem noch nicht unterstützt. - + Testing... teste... - + This is a chan address. You cannot use it as a pseudo-mailing list. - Dies ist eine Chan-Adresse. Sie können sie nicht als Pseudo-Mailingliste verwenden. + - + The address should start with ''BM-'' Die Adresse sollte mit "BM-" beginnen - + The address is not typed or copied correctly (the checksum failed). Die Adresse wurde nicht korrekt getippt oder kopiert (Prüfsumme falsch). - + The version number of this address is higher than this software can support. Please upgrade Bitmessage. - Die Versionsnummer dieser Adresse ist höher als diese Software unterstützt. Bitte installieren Sie die neuste Bitmessage Version. + Die Versionsnummer dieser Adresse ist höher als diese Software unterstützt. Bitte installieren Sie die neueste Bitmessage Version. - + The address contains invalid characters. Diese Adresse beinhaltet ungültige Zeichen. - + Some data encoded in the address is too short. - Die in der Adresse codierten Daten sind zu kurz. + Die in der Adresse kodierten Daten sind zu kurz. - + Some data encoded in the address is too long. - Die in der Adresse codierten Daten sind zu lang. + Die in der Adresse kodierten Daten sind zu lang. - + Some data encoded in the address is malformed. Einige in der Adresse kodierten Daten sind ungültig. - + Enter an address above. - Eine Addresse oben ausfüllen. + - + Address is an old type. We cannot display its past broadcasts. Alter Addressentyp. Wir können deren vorige Rundrufe nicht anzeigen. - + There are no recent broadcasts from this address to display. Es gibt keine neuen Rundrufe von dieser Adresse die angezeigt werden können. - + You are using TCP port %1. (This can be changed in the settings). - Sie benutzen TCP-Port %1 (Dieser kann in den Einstellungen verändert werden). + - + Bitmessage Bitmessage - + Identities Identitäten - + New Identity Neue Identität - + Search Suchen - + All Alle - + To An - + From Von - + Subject Betreff - + Message Nachricht - + Received Erhalten - + Messages Nachrichten - + Address book Addressbuch - + Address Adresse - + Add Contact Kontakt hinzufügen - + Fetch Namecoin ID Hole Namecoin ID - + Subject: Betreff: - + From: Von: - + To: An: - + Send ordinary Message Ordentliche Nachricht senden - + Send Message to your Subscribers Rundruf an Ihre Abonnenten senden - + TTL: Lebenszeit: - + Subscriptions Abonnements - + Add new Subscription Neues Abonnement anlegen - + Chans Chans - + Add Chan Chan hinzufügen - + File Datei - + Settings Einstellungen - + Help Hilfe - + Import keys Schlüssel importieren - + Manage keys Schlüssel verwalten - + Ctrl+Q Strg+Q - + F1 F1 - + Contact support Unterstützung anfordern - + About Über - + Regenerate deterministic addresses Deterministische Adressen neu generieren - + Delete all trashed messages Alle Nachrichten im Papierkorb löschen - + Join / Create chan Chan beitreten / erstellen @@ -1119,67 +1148,67 @@ Sind Sie sicher, dass Sie das Chan löschen möchten? Neuen Eintrag erstellen - + Display the %1 recent broadcast(s) from this address. Die letzten %1 Rundruf(e) von dieser Addresse anzeigen. - + New version of PyBitmessage is available: %1. Download it from https://github.com/Bitmessage/PyBitmessage/releases/latest Neue Version von PyBitmessage steht zur Verfügung: %1. Sie können sie von https://github.com/Bitmessage/PyBitmessage/releases/latest herunterladen. - + Waiting for PoW to finish... %1% Warte auf Abschluss von Berechnungen (PoW)... %1% - + Shutting down Pybitmessage... %1% PyBitmessage wird beendet... %1% - + Waiting for objects to be sent... %1% Warte auf Versand von Objekten... %1% - + Saving settings... %1% Einstellungen werden gespeichert... %1% - + Shutting down core... %1% Kern wird beendet... %1% - + Stopping notifications... %1% Beende Benachrichtigungen... %1% - + Shutdown imminent... %1% Unmittelbar vor Beendung... %1% - + %n hour(s) %n Stunde%n Stunden - + %n day(s) %n Tag%n Tage - + Shutting down PyBitmessage... %1% PyBitmessage wird beendet... %1% - + Sent Gesendet @@ -1224,85 +1253,85 @@ Sind Sie sicher, dass Sie das Chan löschen möchten? Warnung: Datenträger ist voll. Bitmessage wird jetzt beendet. - + Error! Could not find sender address (your address) in the keys.dat file. Fehler! Konnte die Absenderadresse (Ihre Adresse) in der keys.dat-Datei nicht finden. - + Doing work necessary to send broadcast... Arbeit wird verrichtet, um Rundruf zu verschicken... - + Broadcast sent on %1 Rundruf verschickt um %1 - + Encryption key was requested earlier. Verschlüsselungscode wurde früher angefordert. - + Sending a request for the recipient's encryption key. Anfrage nach dem Verschlüsselungscode des Empfängers wird versendet. - + Looking up the receiver's public key Suche nach dem öffentlichen Schlüssel des Empfängers - + Problem: Destination is a mobile device who requests that the destination be included in the message but this is disallowed in your settings. %1 Problem: Der Empfänger benutzt ein mobiles Gerät und erfordert eine unverschlüsselte Empfängeraddresse. Dies ist in Ihren Einstellungen jedoch nicht zulässig. 1% - + Doing work necessary to send message. There is no required difficulty for version 2 addresses like this. Arbeit für Nachrichtenversand wird verrichtet. Version-2-Addressen wie die des Empfängers haben keine Schweirigkeitserforderungen. - + Doing work necessary to send message. Receiver's required difficulty: %1 and %2 Arbeit für Nachrichtenversand wird errichtet. Vom Empfänger geforderte Schwierigkeit: %1 und %2 - + Problem: The work demanded by the recipient (%1 and %2) is more difficult than you are willing to do. %3 Problem: Die vom Empfänger verlangte Arbeit (%1 und %2) ist schwieriger, als Sie in den Einstellungen erlaubt haben. %3 - + Problem: You are trying to send a message to yourself or a chan but your encryption key could not be found in the keys.dat file. Could not encrypt message. %1 Problem: Sie versuchen, eine Nachricht an sich zu versenden, aber Ihr Schlüssel befindet sich nicht in der keys.dat-Datei. Die Nachricht kann nicht verschlüsselt werden. 1% - + Doing work necessary to send message. Arbeit wird verrichtet, um die Nachricht zu verschicken. - + Message sent. Waiting for acknowledgement. Sent on %1 Nachricht gesendet. Auf Bestätigung wird gewartet. Zeitpunkt der Sendung: %1 - + Doing work necessary to request encryption key. Arbeit wird verrichtet, um den Schlüssel nachzufragen... - + Broadcasting the public key request. This program will auto-retry if they are offline. Anfrage nach dem öffentlichen Schlüssel läuft. Wenn der Besitzer nicht mit dem Netzwerk verbunden ist, wird ein Wiederholungsversuch unternommen. - + Sending public key request. Waiting for reply. Requested at %1 Nachfrage nach dem öffentlichen Schlüssel läuft, auf Antwort wird gewartet. Nachgefragt am %1 @@ -1317,37 +1346,37 @@ Receiver's required difficulty: %1 and %2 UPnP Port-Mapping entfernt - + Mark all messages as read Alle Nachrichten als gelesen markieren - + Are you sure you would like to mark all messages read? Sind Sie sicher, dass Sie alle Nachrichten als gelesen markieren möchten? - + Doing work necessary to send broadcast. Führe Arbeit aus, die notwendig ist zum Senden des Rundspruches. - + Proof of work pending Arbeitsbeweis wird berechnet - + %n object(s) pending proof of work %n Objekt wartet auf Berechnungen%n Objekte warten auf Berechnungen - + %n object(s) waiting to be distributed %n Objekt wartet darauf, verteilt zu werden%n Objekte warten darauf, verteilt zu werden - + Wait until these tasks finish? Warten bis diese Aufgaben erledigt sind? @@ -1412,12 +1441,12 @@ Receiver's required difficulty: %1 and %2 Ihre Grafikkarte hat inkorrekt berechnet, OpenCL wird deaktiviert. Bitte benachrichtigen Sie die Entwickler. - + Set notification sound... Benachrichtigungsklang einstellen ... - + Welcome to easy and secure Bitmessage * send messages to other people @@ -1431,130 +1460,140 @@ Willkommen zu einfachem und sicherem Bitmessage * diskutieren Sie mit anderen Leuten in Chans - + not recommended for chans für Chans nicht empfohlen - + Quiet Mode - + stiller Modus - + Problems connecting? Try enabling UPnP in the Network Settings Verbindungsprobleme? Versuchen Sie UPnP in den Netzwerkeinstellungen einzuschalten - + You are trying to send an email instead of a bitmessage. This requires registering with a gateway. Attempt to register? Sie versuchen, eine E-Mail anstelle einer Bitmessage zu senden. Dies erfordert eine Registrierung bei einer Schnittstelle. Registrierung versuchen? - + Error: Bitmessage addresses start with BM- Please check the recipient address %1 Fehler: Bitmessage Adressen starten mit BM- Bitte überprüfen Sie die Empfängeradresse %1 - + Error: The recipient address %1 is not typed or copied correctly. Please check it. Fehler: Die Empfängeradresse %1 wurde nicht korrekt getippt oder kopiert. Bitte überprüfen. - + Error: The recipient address %1 contains invalid characters. Please check it. Fehler: Die Empfängeradresse %1 beinhaltet ungültig Zeichen. Bitte überprüfen. - + Error: The version of the recipient address %1 is too high. Either you need to upgrade your Bitmessage software or your acquaintance is being clever. Fehler: Die Empfängerdresseversion von %1 ist zu hoch. Entweder Sie müssen Ihre Bitmessage Software aktualisieren oder Ihr Bekannter ist sehr clever. - + Error: Some data encoded in the recipient address %1 is too short. There might be something wrong with the software of your acquaintance. Fehler: Einige Daten die in der Empfängerdresse %1 codiert sind, sind zu kurz. Es könnte sein, dass etwas mit der Software Ihres Bekannten nicht stimmt. - + Error: Some data encoded in the recipient address %1 is too long. There might be something wrong with the software of your acquaintance. Fehler: Einige Daten die in der Empfängeradresse %1 codiert sind, sind zu lang. Es könnte sein, dass etwas mit der Software Ihres Bekannten nicht stimmt. - + Error: Some data encoded in the recipient address %1 is malformed. There might be something wrong with the software of your acquaintance. Fehler: Einige codierte Daten in der Empfängeradresse %1 sind ungültig. Es könnte etwas mit der Software Ihres Bekannten sein. - + Error: Something is wrong with the recipient address %1. Fehler: Mit der Empfängeradresse %1 stimmt etwas nicht. - + Error: %1 Fehler: %1 - + From %1 Von %1 - + Synchronisation pending Synchronisierung läuft - + Bitmessage hasn't synchronised with the network, %n object(s) to be downloaded. If you quit now, it may cause delivery delays. Wait until the synchronisation finishes? Bitmessage ist nicht synchronisiert, %n Objekt wurde noch nicht heruntergeladen. Wenn Sie das Programm jetzt beenden, kann es zu Zustellverzögerungen kommen. Warten bis die Synchronisierung abgeschlossen ist?Bitmessage ist nicht synchronisiert, %n Objekte wurden noch nicht heruntergeladen. Wenn Sie das Programm jetzt beenden, kann es zu Zustellverzögerungen kommen. Warten bis die Synchronisierung abgeschlossen ist? - + Not connected Nicht verbunden - + Bitmessage isn't connected to the network. If you quit now, it may cause delivery delays. Wait until connected and the synchronisation finishes? Bitmessage ist nicht mit dem Netzwerk verbunden. Wenn Sie das Programm jetzt beenden, kann es zu Zustellverzögerungen kommen. Warten bis eine Verbindung besteht und die Synchronisierung abgeschlossen ist? - + Waiting for network connection... Warte auf Netzwerkverbindung... - + Waiting for finishing synchronisation... Warte auf Synchronisationsabschluss... - + You have already set a notification sound for this address book entry. Do you really want to overwrite it? Sie haben bereits einen Benachrichtigungsklang für diesen Adressbucheintrag gesetzt. Möchten Sie ihn wirklich überschreiben? + + + Go online + Mit dem Netzwerk verbinden + + + + Go offline + Trennen vom Netzwerk + MessageView - + Follow external link Externen Link folgen - + The link "%1" will open in a browser. It may be a security risk, it could de-anonymise you or download malicious data. Are you sure? Der Link "%1" wird in Browser geöffnet. Es kann ein Sicherheitsrisiko darstellen, es könnte Sie de-anonymisieren oder schädliche Aktivitäten durchführen. Sind Sie sicher? - + HTML detected, click here to display HTML gefunden, klicken Sie hier um anzuzeigen - + Click here to disable HTML Klicken Sie hier um HTML-Anzeige zu deaktivieren @@ -1577,99 +1616,99 @@ Womöglich sollten Sie Bitmessage upgraden. NewAddressDialog - + Create new Address Neue Adresse erstellen - + Here you may generate as many addresses as you like. Indeed, creating and abandoning addresses is encouraged. You may generate addresses by using either random numbers or by using a passphrase. If you use a passphrase, the address is called a "deterministic" address. The 'Random Number' option is selected by default but deterministic addresses have several pros and cons: Sie können so viele Adressen generieren wie Sie möchten. Es ist sogar empfohlen neue Adressen zu verwenden und alte fallen zu lassen. Sie können Adressen durch Zufallszahlen erstellen lassen, oder unter Verwendung eines Kennwortsatzes. Wenn Sie einen Kennwortsatz verwenden, nennt man dies eine "deterministische" Adresse. Die Zufallszahlen-Option ist standardmässig gewählt, jedoch haben deterministische Adressen einige Vor- und Nachteile: - + <html><head/><body><p><span style=" font-weight:600;">Pros:<br/></span>You can recreate your addresses on any computer from memory. <br/>You need-not worry about backing up your keys.dat file as long as you can remember your passphrase. <br/><span style=" font-weight:600;">Cons:<br/></span>You must remember (or write down) your passphrase if you expect to be able to recreate your keys if they are lost. <br/>You must remember the address version number and the stream number along with your passphrase. <br/>If you choose a weak passphrase and someone on the Internet can brute-force it, they can read your messages and send messages as you.</p></body></html> <html><head/><body><p><span style=" font-weight:600;">Vorteile:<br/></span>Sie können ihre Adresse an jedem Computer aus dem Gedächtnis regenerieren. <br/>Sie brauchen sich keine Sorgen um das Sichern ihrer Schlüssel machen solange Sie sich den Kennwortsatz merken. <br/><span style=" font-weight:600;">Nachteile:<br/></span>Sie müssen sich den Kennwortsatz merken (oder aufschreiben) wenn Sie in der Lage sein wollen ihre Schlüssel wiederherzustellen. <br/>Sie müssen sich die Adressversion und die Datenstrom Nummer zusammen mit dem Kennwortsatz merken. <br/>Wenn Sie einen schwachen Kennwortsatz wählen und jemand kann ihn erraten oder durch ausprobieren herausbekommen, kann dieser Ihre Nachrichten lesen, oder in ihrem Namen Nachrichten senden..</p></body></html> - + Use a random number generator to make an address Lassen Sie eine Adresse mittels Zufallsgenerator erstellen - + Use a passphrase to make addresses Benutzen Sie einen Kennwortsatz um eine Adresse erstellen zu lassen - + Spend several minutes of extra computing time to make the address(es) 1 or 2 characters shorter Verwenden Sie einige Minuten extra Rechenleistung um die Adresse(n) ein bis zwei Zeichen kürzer zu machen - + Make deterministic addresses Deterministische Adresse erzeugen - + Address version number: 4 Adress-Versionsnummer: 4 - + In addition to your passphrase, you must remember these numbers: Zusätzlich zu Ihrem Kennwortsatz müssen Sie sich diese Zahlen merken: - + Passphrase Kennwortsatz - + Number of addresses to make based on your passphrase: Anzahl Adressen die basierend auf diesem Kennwortsatz erzeugt werden sollen: - + Stream number: 1 Datenstrom Nummer: 1 - + Retype passphrase Kennwortsatz erneut eingeben - + Randomly generate address Zufällig generierte Adresse - + Label (not shown to anyone except you) Bezeichnung (Wird niemandem außer Ihnen gezeigt) - + Use the most available stream Verwendung des am besten verfügbaren Datenstroms - + (best if this is the first of many addresses you will create) (Zum Generieren der ersten Adresse empfohlen) - + Use the same stream as an existing address Verwendung des gleichen Datenstroms wie eine bestehende Adresse - + (saves you some bandwidth and processing power) (Dies erspart Ihnen etwas an Bandbreite und Rechenleistung) @@ -1677,22 +1716,22 @@ Die Zufallszahlen-Option ist standardmässig gewählt, jedoch haben deterministi NewSubscriptionDialog - + Add new entry Neuen Eintrag erstellen - + Label Name oder Bezeichnung - + Address Adresse - + Enter an address above. Bitte geben Sie oben eine Adresse ein. @@ -1700,55 +1739,60 @@ Die Zufallszahlen-Option ist standardmässig gewählt, jedoch haben deterministi SpecialAddressBehaviorDialog - + Special Address Behavior Spezielles Adressverhalten - + Behave as a normal address Wie eine normale Adresse verhalten - + Behave as a pseudo-mailing-list address Wie eine Pseudo-Mailinglistenadresse verhalten - + Mail received to a pseudo-mailing-list address will be automatically broadcast to subscribers (and thus will be public). Nachrichten an eine Pseudo-Mailinglistenadresse werden automatisch an alle Abonnenten weitergeleitet (Der Inhalt ist dann öffentlich). - + Name of the pseudo-mailing-list: Name der Pseudo-Mailingliste: + + + This is a chan address. You cannot use it as a pseudo-mailing list. + Dies ist eine Chan-Adresse. Sie können sie nicht als Pseudo-Mailingliste verwenden. + aboutDialog - + About Über - + PyBitmessage - PyBitmessage + - + version ? - Version ? + - + <html><head/><body><p>Distributed under the MIT/X11 software license; see <a href="http://www.opensource.org/licenses/mit-license.php"><span style=" text-decoration: underline; color:#0000ff;">http://www.opensource.org/licenses/mit-license.php</span></a></p></body></html> <html><head/><body><p>Veröffentlicht unter der MIT/X11 Software-Lizenz, siehe unter <a href="http://www.opensource.org/licenses/mit-license.php"><span style=" text-decoration: underline; color:#0000ff;">http://www.opensource.org/licenses/mit-license.php</span></a></p></body></html> - + This is Beta software. Dies ist Beta-Software. @@ -1757,10 +1801,10 @@ Die Zufallszahlen-Option ist standardmässig gewählt, jedoch haben deterministi <html><head/><body><p>Copyright © 2012-2016 Jonathan Warren<br/>Copyright © 2013-2016 The Bitmessage Developers</p></body></html> - - - <html><head/><body><p>Copyright &copy; 2012-2016 Jonathan Warren<br/>Copyright &copy; 2013-2016 The Bitmessage Developers</p></body></html> - <html><head/><body><p>Copyright &copy; 2012-2016 Jonathan Warren<br/>Copyright &copy; 2013-2016 Die Bitmessage-Entwickler</p></body></html> + + + <html><head/><body><p>Copyright © 2012-2016 Jonathan Warren<br/>Copyright © 2013-2017 The Bitmessage Developers</p></body></html> + <html><head/><body><p>Copyright © 2012-2016 Jonathan Warren<br/>Copyright © 2013-2017 The Bitmessage Developers</p></body></html> @@ -1804,40 +1848,45 @@ Die Zufallszahlen-Option ist standardmässig gewählt, jedoch haben deterministi connectDialog - + Bitmessage Bitmessage - + Bitmessage won't connect to anyone until you let it. Bitmessage wird sich nicht verbinden, wenn Sie es nicht möchten. - + Connect now Jetzt verbinden - + Let me configure special network settings first Zunächst spezielle Netzwerkeinstellungen vornehmen + + + Work offline + Nicht verbinden + helpDialog - + Help Hilfe - + <a href="https://bitmessage.org/wiki/PyBitmessage_Help">https://bitmessage.org/wiki/PyBitmessage_Help</a> <a href="https://bitmessage.org/wiki/PyBitmessage_Help">https://bitmessage.org/wiki/PyBitmessage_Help</a> - + As Bitmessage is a collaborative project, help can be found online in the Bitmessage Wiki: Bitmessage ist ein kollaboratives Projekt. Hilfe finden Sie online im Bitmessage-Wiki: @@ -1845,30 +1894,35 @@ Die Zufallszahlen-Option ist standardmässig gewählt, jedoch haben deterministi iconGlossaryDialog - + Icon Glossary Icon Glossar - + You have no connections with other peers. Sie haben keine Verbindung mit anderen Netzwerkteilnehmern. - + You have made at least one connection to a peer using an outgoing connection but you have not yet received any incoming connections. Your firewall or home router probably isn't configured to forward incoming TCP connections to your computer. Bitmessage will work just fine but it would help the Bitmessage network if you allowed for incoming connections and will help you be a better-connected node. Sie haben mindestes eine Verbindung mit einem Netzwerkteilnehmer über eine ausgehende Verbindung, aber Sie haben noch keine eingehende Verbindung. Ihre Firewall oder Ihr Router ist vermutlich nicht richtig konfiguriert, um eingehende TCP-Verbindungen an Ihren Computer weiterzuleiten. Bitmessage wird gut funktionieren, jedoch helfen Sie dem Netzwerk, wenn Sie eingehende Verbindungen erlauben. Es hilft auch Ihnen, schneller und mehr Verbindungen ins Netzwerk aufzubauen. You are using TCP port ?. (This can be changed in the settings). - Sie benutzen TCP-Port ?. (Dies kann in den Einstellungen verändert werden). + - + You do have connections with other peers and your firewall is correctly configured. Sie haben Verbindungen mit anderen Netzwerkteilnehmern und Ihre Firewall ist richtig konfiguriert. + + + You are using TCP port %1. (This can be changed in the settings). + Sie benutzen TCP-Port %1 (Dieser kann in den Einstellungen verändert werden). + networkstatus @@ -1953,7 +2007,7 @@ Die Zufallszahlen-Option ist standardmässig gewählt, jedoch haben deterministi Herunter: 0 kB/s - + Network Status Netzwerkstatus @@ -2109,12 +2163,12 @@ Die Zufallszahlen-Option ist standardmässig gewählt, jedoch haben deterministi Chan %1 erfolgreich erstellt/beigetreten - + Chan creation / joining failed Chan-erstellung/-beitritt fehlgeschlagen - + Chan creation / joining cancelled Chan-erstellung/-beitritt abgebrochen @@ -2148,52 +2202,52 @@ Die Zufallszahlen-Option ist standardmässig gewählt, jedoch haben deterministi regenerateAddressesDialog - + Regenerate Existing Addresses Bestehende Adresse regenerieren - + Regenerate existing addresses Bestehende Adresse regenerieren - + Passphrase Kennwortsatz - + Number of addresses to make based on your passphrase: - Anzahl der Adressen, die basierend auf diesem Kennwortsatz erzeugt werden sollen: + Anzahl Adressen die basierend auf diesem Kennwortsatz erzeugt werden sollen: - + Address version number: Adress-Versionsnummer: - + Stream number: Stream-Nummer: - + 1 1 - + Spend several minutes of extra computing time to make the address(es) 1 or 2 characters shorter - Verwenden Sie einige Minuten extra Rechenleistung, um die Adresse(n) ein bis zwei Zeichen kürzer zu machen + Verwenden Sie einige Minuten extra Rechenleistung um die Adresse(n) ein bis zwei Zeichen kürzer zu machen - + You must check (or not check) this box just like you did (or didn't) when you made your addresses the first time. Sie müssen diese Option auswählen (oder nicht auswählen) wie Sie es gemacht haben, als Sie Ihre Adresse das erste Mal erstellt haben. - + If you have previously made deterministic addresses but lost them due to an accident (like hard drive failure), you can regenerate them here. If you used the random number generator to make your addresses then this form will be of no use to you. Wenn Sie bereits deterministische Adressen erstellt haben, aber diese durch einen Unfall (zum Beispiel durch eine defekte Festplatte) verloren haben, können Sie sie hier regenerieren. Dies funktioniert nur dann, wenn Sie bei der erstmaligen Erstellung Ihrer Adressen nicht den Zufallsgenerator verwendet haben. From 9db0f5bcb3521222f6518bd2226131fd7db11f7d Mon Sep 17 00:00:00 2001 From: PyBitmessage Translations Date: Fri, 2 Feb 2018 19:22:18 +0100 Subject: [PATCH 377/407] Auto-updated language sk from transifex --- src/translations/bitmessage_sk.qm | Bin 89951 -> 88287 bytes src/translations/bitmessage_sk.ts | 750 +++++++++++++++++------------- 2 files changed, 417 insertions(+), 333 deletions(-) diff --git a/src/translations/bitmessage_sk.qm b/src/translations/bitmessage_sk.qm index 525f4b38b7718e499d3278aec4920621a50ed13a..228eff26de773d42b59882b0de916e0c870184f6 100644 GIT binary patch delta 4544 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 efWQIa AddAddressDialog - + Add new entry Pridať nový záznam - + Label Označenie - + Address Adresa @@ -20,70 +20,99 @@ EmailGatewayDialog - + Email gateway E-mailová brána - + Register on email gateway Registrácia na e-mailovej bráne - + Account status at email gateway Stav účtu na e-mailovej bráne - + Change account settings at email gateway Zmena nastavení účtu na e-mailovej bráne - + Unregister from email gateway Zrušiť registráciu na e-mailovej bráne - + Email gateway allows you to communicate with email users. Currently, only the Mailchuck email gateway (@mailchuck.com) is available. E-mailové brány umožňujú komunikovať s užívateľmi e-mailu. Momentálne je k dispozícii iba e-mailová brána Mailchuck (@mailchuck.com). - + Desired email address (including @mailchuck.com): Požadovaná e-mailová adresa (vrátane @ mailchuck.com): + + + @mailchuck.com + @mailchuck.com + + + + Registration failed: + Registrácia zlyhala: + + + + The requested email address is not available, please try a new one. + Požadovaná e-mailová adresa nie je k dispozícii, skúste znova. + + + + Sending email gateway registration request + Odosielam žiadosť o registráciu na e-mailovej bráne + + + + Sending email gateway unregistration request + Odosielam žiadosť o odhlásenie z e-mailovej brány + + + + Sending email gateway status request + Odosielam žiadosť o stav e-mailovej brány + EmailGatewayRegistrationDialog - + Registration failed: - Registrácia zlyhala: + - + The requested email address is not available, please try a new one. Fill out the new desired email address (including @mailchuck.com) below: - Požadovaná e-mailová adresa nie je k dispozícii, skúste znova. Vyplňte novú požadovanú e-mailovú adresu (vrátane @mailchuck.com) nižšie: + Email gateway registration - Registrácia na e-mailovej bráne + Email gateway allows you to communicate with email users. Currently, only the Mailchuck email gateway (@mailchuck.com) is available. Please type the desired email address (including @mailchuck.com) below: - E-mailové brány umožňujú komunikovať s užívateľmi e-mailu. Momentálne je k dispozícii iba e-mailová brána Mailchuck (@mailchuck.com). -Vyplňte požadovanú e-mailovú adresu (vrátane @mailchuck.com) nižšie: + Mailchuck - + # You can use this to configure your email gateway account # Uncomment the setting you want to use # Here are the options: @@ -171,52 +200,52 @@ Vyplňte požadovanú e-mailovú adresu (vrátane @mailchuck.com) nižšie: MainWindow - + Reply to sender Odpovedať odosielateľovi - + Reply to channel Odpoveď na kanál - + Add sender to your Address Book Pridať odosielateľa do adresára - + Add sender to your Blacklist Pridať odosielateľa do svojho zoznamu zakázaných - + Move to Trash Presunúť do koša - + Undelete Obnoviť - + View HTML code as formatted text Zobraziť HTML kód ako formátovaný text - + Save message as... Uložiť správu ako... - + Mark Unread Označiť ako neprečítané - + New Nová @@ -241,12 +270,12 @@ Vyplňte požadovanú e-mailovú adresu (vrátane @mailchuck.com) nižšie:Kopírovať adresu do clipboardu - + Special address behavior... Zvláštne správanie adresy... - + Email gateway E-mailová brána @@ -256,37 +285,37 @@ Vyplňte požadovanú e-mailovú adresu (vrátane @mailchuck.com) nižšie:Zmazať - + Send message to this address Poslať správu na túto adresu - + Subscribe to this address Prihlásiť sa k odberu tejto adresy - + Add New Address Pridať novú adresu - + Copy destination address to clipboard Kopírovať cieľovú adresu do clipboardu - + Force send Vynútiť odoslanie - + One of your addresses, %1, is an old version 1 address. Version 1 addresses are no longer supported. May we delete it now? Jedna z vašich adries, %1, je stará verzia adresy, 1. Verzie adresy 1 už nie sú podporované. Odstrániť ju teraz? - + Waiting for their encryption key. Will request it again soon. Čakanie na šifrovací kľúč príjemcu. Čoskoro bude vyžiadaný znova. @@ -296,17 +325,17 @@ Vyplňte požadovanú e-mailovú adresu (vrátane @mailchuck.com) nižšie: - + Queued. Vo fronte. - + Message sent. Waiting for acknowledgement. Sent at %1 Správa odoslaná. Čakanie na potvrdenie. Odoslaná %1 - + Message sent. Sent at %1 Správa odoslaná. Odoslaná %1 @@ -316,77 +345,77 @@ Vyplňte požadovanú e-mailovú adresu (vrátane @mailchuck.com) nižšie: - + Acknowledgement of the message received %1 Potvrdenie prijatia správy %1 - + Broadcast queued. Rozoslanie vo fronte. - + Broadcast on %1 Rozoslané 1% - + Problem: The work demanded by the recipient is more difficult than you are willing to do. %1 Problém: práca požadovná príjemcom je oveľa ťažšia, než je povolené v nastaveniach. %1 - + Problem: The recipient's encryption key is no good. Could not encrypt message. %1 Problém: šifrovací kľúč príjemcu je nesprávny. Nie je možné zašifrovať správu. %1 - + Forced difficulty override. Send should start soon. Obmedzenie obtiažnosti práce zrušené. Odosielanie by malo čoskoro začať. - + Unknown status: %1 %2 Neznámy stav: %1 %2 - + Not Connected Nepripojený - + Show Bitmessage Ukázať Bitmessage - + Send Odoslať - + Subscribe Prihlásiť sa k odberu - + Channel Kanál - + Quit Ukončiť - + You may manage your keys by editing the keys.dat file stored in the same directory as this program. It is important that you back up this file. Kľúče môžete spravovať úpravou súboru keys.dat, ktorý je uložený v rovnakom adresári ako tento program. Tento súbor je dôležité zálohovať. - + You may manage your keys by editing the keys.dat file stored in %1 It is important that you back up this file. @@ -395,17 +424,17 @@ It is important that you back up this file. Tento súbor je dôležité zálohovať. - + Open keys.dat? Otvoriť keys.dat? - + You may manage your keys by editing the keys.dat file stored in the same directory as this program. It is important that you back up this file. Would you like to open the file now? (Be sure to close Bitmessage before making any changes.) Kľúče môžete spravovať úpravou súboru keys.dat, ktorý je uložený v rovnakom adresári ako tento program. Tento súbor je dôležité zálohovať. Chcete tento súbor teraz otvoriť? (Nezabudnite zatvoriť Bitmessage pred vykonaním akýchkoľvek zmien.) - + You may manage your keys by editing the keys.dat file stored in %1 It is important that you back up this file. Would you like to open the file now? (Be sure to close Bitmessage before making any changes.) @@ -414,37 +443,37 @@ It is important that you back up this file. Would you like to open the file now? Tento súbor je dôležité zálohovať. Chcete tento súbor teraz otvoriť? (Nezabudnite zatvoriť Bitmessage pred vykonaním akýchkoľvek zmien.) - + Delete trash? Vyprázdniť kôš? - + Are you sure you want to delete all trashed messages? Ste si istí, že chcete všetky správy z koša odstrániť? - + bad passphrase zlé heslo - + You must type your passphrase. If you don't have one then this is not the form for you. Je nutné zadať prístupové heslo. Ak heslo nemáte, tento formulár nie je pre Vás. - + Bad address version number Nesprávne číslo verzie adresy - + Your address version number must be a number: either 3 or 4. Číslo verzie adresy musí byť číslo: buď 3 alebo 4. - + Your address version number must be either 3 or 4. Vaše číslo verzie adresy musí byť buď 3 alebo 4. @@ -514,22 +543,22 @@ Tento súbor je dôležité zálohovať. Chcete tento súbor teraz otvoriť? (Ne - + Connection lost Spojenie bolo stratené - + Connected Spojený - + Message trashed Správa odstránenia - + The TTL, or Time-To-Live is the length of time that the network will hold the message. The recipient must get it during this time. If your Bitmessage client does not hear an acknowledgement, it will resend the message automatically. The longer the Time-To-Live, the @@ -537,17 +566,17 @@ Tento súbor je dôležité zálohovať. Chcete tento súbor teraz otvoriť? (Ne TTL (doba životnosti) je čas, počas ktorého bude sieť udržiavať správu. Príjemca musí správu prijať počas tejto životnosti. Keď odosielateľov Bitmessage nedostane po vypršaní životnosti potvrdenie o prijatí, automaticky správu odošle znova. Čím vyššia doba životnosti, tým viac práce musí počítač odosielateľa vykonat na odoslanie správy. Zvyčajne je vhodná doba životnosti okolo štyroch-piatich dní. - + Message too long Správa je príliš dlhá - + The message that you are trying to send is too long by %1 bytes. (The maximum is 261644 bytes). Please cut it down before sending. Správa, ktorú skúšate poslať, má %1 bajtov naviac. (Maximum je 261 644 bajtov). Prosím pred odoslaním skrátiť. - + Error: Your account wasn't registered at an email gateway. Sending registration now as %1, please wait for the registration to be processed before retrying sending. Chyba: Váš účet nebol registrovaný na e-mailovej bráne. Skúšam registrovať ako %1, prosím počkajte na spracovanie registrácie pred opakovaným odoslaním správy. @@ -592,69 +621,69 @@ Tento súbor je dôležité zálohovať. Chcete tento súbor teraz otvoriť? (Ne - + Error: You must specify a From address. If you don't have one, go to the 'Your Identities' tab. Chyba: musíte zadať adresu "Od". Ak žiadnu nemáte, prejdite na kartu "Vaše identity". - + Address version number Číslo verzie adresy - + Concerning the address %1, Bitmessage cannot understand address version numbers of %2. Perhaps upgrade Bitmessage to the latest version. Čo sa týka adresy %1, Bitmessage nepozná číslo verzie adresy %2. Možno by ste mali upgradenúť Bitmessage na najnovšiu verziu. - + Stream number Číslo prúdu - + Concerning the address %1, Bitmessage cannot handle stream numbers of %2. Perhaps upgrade Bitmessage to the latest version. Čo sa týka adresy %1, Bitmessage nespracováva číslo prúdu %2. Možno by ste mali upgradenúť Bitmessage na najnovšiu verziu. - + Warning: You are currently not connected. Bitmessage will do the work necessary to send the message but it won't send until you connect. Upozornenie: momentálne nie ste pripojení. Bitmessage vykoná prácu potrebnú na odoslanie správy, ale odoslať ju môže, až keď budete pripojení. - + Message queued. Správa vo fronte. - + Your 'To' field is empty. Pole "Komu" je prázdne. - + Right click one or more entries in your address book and select 'Send message to this address'. Vybertie jednu alebo viacero položiek v adresári, pravým tlačidlom myši zvoľte "Odoslať správu na túto adresu". - + Fetched address from namecoin identity. Prebratá adresa z namecoin-ovej identity. - + New Message Nová správa - + From - Od + - + Sending email gateway registration request - Odosielam požiadavku o registráciu na e-mailovej bráne + @@ -667,142 +696,142 @@ Tento súbor je dôležité zálohovať. Chcete tento súbor teraz otvoriť? (Ne Zadaná adresa bola neplatná a bude ignorovaná. - + Error: You cannot add the same address to your address book twice. Try renaming the existing one if you want. Chyba: tú istú adresu nemožno pridať do adresára dvakrát. Ak chcete, môžete skúsiť premenovať existujúcu menovku. - + Error: You cannot add the same address to your subscriptions twice. Perhaps rename the existing one if you want. Chyba: nemožno pridať rovnakú adresu k odberu dvakrát. Keď chcete, môžete premenovať existujúci záznam. - + Restart Reštart - + You must restart Bitmessage for the port number change to take effect. Aby sa zmena čísla portu prejavila, musíte reštartovať Bitmessage. - + Bitmessage will use your proxy from now on but you may want to manually restart Bitmessage now to close existing connections (if any). Bitmessage bude odteraz používať proxy, ale ak chcete ukončiť existujúce spojenia, musíte Bitmessage manuálne reštartovať. - + Number needed Číslo potrebné - + Your maximum download and upload rate must be numbers. Ignoring what you typed. Maxímálna rýchlosť príjmu a odoslania musí byť uvedená v číslach. Ignorujem zadané údaje. - + Will not resend ever Nikdy opätovne neodosielať - + Note that the time limit you entered is less than the amount of time Bitmessage waits for the first resend attempt therefore your messages will never be resent. Upozornenie: časový limit, ktorý ste zadali, je menší ako čas, ktorý Bitmessage čaká na prvý pokus o opätovné zaslanie, a preto vaše správy nebudú nikdy opätovne odoslané. - + Sending email gateway unregistration request - Odosielam žiadosť o odhlásenie z e-mailovej brány + - + Sending email gateway status request - Odosielam požiadavku o stave e-mailovej brány + - + Passphrase mismatch Nezhoda hesla - + The passphrase you entered twice doesn't match. Try again. Zadané heslá sa rôznia. Skúste znova. - + Choose a passphrase Vyberte heslo - + You really do need a passphrase. Heslo je skutočne potrebné. - + Address is gone Adresa zmizla - + Bitmessage cannot find your address %1. Perhaps you removed it? Bitmessage nemôže nájsť vašu adresu %1. Možno ste ju odstránili? - + Address disabled Adresa deaktivovaná - + Error: The address from which you are trying to send is disabled. You'll have to enable it on the 'Your Identities' tab before using it. Chyba: adresa, z ktorej sa pokúšate odoslať, je neaktívna. Pred použitím ju musíte aktivovať v karte "Vaše identity". - + Entry added to the Address Book. Edit the label to your liking. - Záznam pridaný do adresára. Upravte označenie podľa vašich predstáv. + - + Entry added to the blacklist. Edit the label to your liking. Záznam pridaný na zoznam zakázaných. Upravte označenie podľa vašich predstáv. - + Error: You cannot add the same address to your blacklist twice. Try renaming the existing one if you want. Chyba: tú istú adresu nemožno pridať na zoznam zakázaných dvakrát. Ak chcete, môžete skúsiť premenovať existujúce označenie. - + Moved items to trash. Položky presunuté do koša. - + Undeleted item. Položka obnovená. - + Save As... Uložiť ako... - + Write error. Chyba pri zapisovaní. - + No addresses selected. Nevybraná žiadna adresa. - + If you delete the subscription, messages that you already received will become inaccessible. Maybe you can consider disabling the subscription instead. Disabled subscriptions will not receive new messages, but you can still view messages you already received. Are you sure you want to delete the subscription? @@ -811,7 +840,7 @@ Are you sure you want to delete the subscription? Ste si istý, že chcete odber odstrániť? - + If you delete the channel, messages that you already received will become inaccessible. Maybe you can consider disabling the channel instead. Disabled channels will not receive new messages, but you can still view messages you already received. Are you sure you want to delete the channel? @@ -820,277 +849,277 @@ Are you sure you want to delete the channel? Ste si istý, že chcete kanál odstrániť? - + Do you really want to remove this avatar? Naozaj chcete odstrániť tento avatar? - + You have already set an avatar for this address. Do you really want to overwrite it? Pre túto adresu ste už ste nastavili avatar. Naozaj ho chcete ho zmeniť? - + Start-on-login not yet supported on your OS. Spustenie pri prihlásení zatiaľ pre váš operačný systém nie je podporované. - + Minimize-to-tray not yet supported on your OS. Minimalizovanie do panelu úloh zatiaľ pre váš operačný systém nie je podporované. - + Tray notifications not yet supported on your OS. Oblasť oznámení zatiaľ pre váš operačný systém nie je podporovaná. - + Testing... Testujem... - + This is a chan address. You cannot use it as a pseudo-mailing list. - Toto je adresa kanálu. Nie je možné ju používať ako pseudo poštový zoznam. + - + The address should start with ''BM-'' Adresa by mala začínať ''BM-'' - + The address is not typed or copied correctly (the checksum failed). Nesprávne zadaná alebo skopírovaná adresa (kontrolný súčet zlyhal). - + The version number of this address is higher than this software can support. Please upgrade Bitmessage. Číslo verzie tejto adresy je vyššie ako tento softvér podporuje. Prosím inovujte Bitmessage. - + The address contains invalid characters. Adresa obsahuje neplatné znaky. - + Some data encoded in the address is too short. Niektoré dáta zakódované v adrese sú príliš krátke. - + Some data encoded in the address is too long. Niektoré dáta zakódované v adrese sú príliš dlhé. - + Some data encoded in the address is malformed. Niektoré dáta zakódované v adrese sú poškodené. - + Enter an address above. - Zadajte adresu vyššie. + - + Address is an old type. We cannot display its past broadcasts. Starý typ adresy. Nie je možné zobraziť jej predchádzajúce hromadné správy. - + There are no recent broadcasts from this address to display. Neboli nájdené žiadne nedávne hromadé správy z tejto adresy. - + You are using TCP port %1. (This can be changed in the settings). - Používate port TCP %1. (Možno zmeniť v nastaveniach). + - + Bitmessage Bitmessage - + Identities Identity - + New Identity Nová identita - + Search Hľadaj - + All Všetky - + To Príjemca - + From Odosielateľ - + Subject Predmet - + Message Správa - + Received Prijaté - + Messages Správy - + Address book Adresár - + Address Adresa - + Add Contact Pridať kontakt - + Fetch Namecoin ID Získať identifikátor namecoin-u - + Subject: Predmet: - + From: Odosielateľ: - + To: Príjemca: - + Send ordinary Message Poslať obyčajnú správu - + Send Message to your Subscribers Poslať správu vašim odberateľom - + TTL: Doba životnosti: - + Subscriptions Odbery - + Add new Subscription Pridať nový odber - + Chans Kanály - + Add Chan Pridať kanál - + File Súbor - + Settings Nastavenia - + Help Pomoc - + Import keys Importovať kľúče - + Manage keys Spravovať kľúče - + Ctrl+Q Ctrl+Q - + F1 F1 - + Contact support Kontaktovať používateľskú podporu - + About O - + Regenerate deterministic addresses Znova vytvoriť deterministické adresy - + Delete all trashed messages Odstrániť všetky správy z koša - + Join / Create chan Pripojiť / vytvoriť kanál @@ -1115,67 +1144,67 @@ Ste si istý, že chcete kanál odstrániť? Pridať nový záznam - + Display the %1 recent broadcast(s) from this address. Zobraziť posledných %1 hromadných správ z tejto adresy. - + New version of PyBitmessage is available: %1. Download it from https://github.com/Bitmessage/PyBitmessage/releases/latest K dispozícii je nová verzia PyBitmessage: %1. Môžete ju stiahnuť na https://github.com/Bitmessage/PyBitmessage/releases/latest - + Waiting for PoW to finish... %1% Čakám na ukončenie práce... %1% - + Shutting down Pybitmessage... %1% Ukončujem PyBitmessage... %1% - + Waiting for objects to be sent... %1% Čakám na odoslanie objektov... %1% - + Saving settings... %1% Ukladám nastavenia... %1% - + Shutting down core... %1% Ukončujem jadro... %1% - + Stopping notifications... %1% Zastavujem oznámenia... %1% - + Shutdown imminent... %1% Posledná fáza ukončenia... %1% - + %n hour(s) %n hodina%n hodiny%n hodín - + %n day(s) %n deň%n dni%n dní - + Shutting down PyBitmessage... %1% Ukončujem PyBitmessage... %1% - + Sent Odoslané @@ -1220,86 +1249,86 @@ Ste si istý, že chcete kanál odstrániť? Upozornenie: Váš disk alebo priestor na ukladanie dát je plný. Bitmessage bude teraz ukončený. - + Error! Could not find sender address (your address) in the keys.dat file. Chyba! Nemožno nájsť adresu odosielateľa (vašu adresu) v súbore keys.dat. - + Doing work necessary to send broadcast... Vykonávam prácu potrebnú na rozoslanie... - + Broadcast sent on %1 Rozoslané %1 - + Encryption key was requested earlier. Šifrovací klúč bol vyžiadaný. - + Sending a request for the recipient's encryption key. Odosielam požiadavku na kľúč príjemcu. - + Looking up the receiver's public key Hľadám príjemcov verejný kľúč - + Problem: Destination is a mobile device who requests that the destination be included in the message but this is disallowed in your settings. %1 Problém: adresa príjemcu je na mobilnom zariadení a požaduje, aby správy obsahovali nezašifrovanú adresu príjemcu. Vaše nastavenia však túto možnost nemajú povolenú. %1 - + Doing work necessary to send message. There is no required difficulty for version 2 addresses like this. Vykonávam prácu potrebnú na odoslanie správy. Adresy verzie dva, ako táto, nepožadujú obtiažnosť. - + Doing work necessary to send message. Receiver's required difficulty: %1 and %2 Vykonávam prácu potrebnú na odoslanie správy. Priímcova požadovaná obtiažnosť: %1 a %2 - + Problem: The work demanded by the recipient (%1 and %2) is more difficult than you are willing to do. %3 Problém: Práca požadovná príjemcom (%1 a %2) je obtiažnejšia, ako máte povolené. %3 - + Problem: You are trying to send a message to yourself or a chan but your encryption key could not be found in the keys.dat file. Could not encrypt message. %1 Problém: skúšate odslať správu sami sebe, ale nemôžem nájsť šifrovací kľúč v súbore keys.dat. Nemožno správu zašifrovať: %1 - + Doing work necessary to send message. Vykonávam prácu potrebnú na odoslanie... - + Message sent. Waiting for acknowledgement. Sent on %1 Správa odoslaná. Čakanie na potvrdenie. Odoslaná %1 - + Doing work necessary to request encryption key. Vykonávam prácu potrebnú na vyžiadanie šifrovacieho kľúča. - + Broadcasting the public key request. This program will auto-retry if they are offline. Rozosielam požiadavku na verejný kľúč. Ak nebude príjemca spojený zo sieťou, budem skúšať znova. - + Sending public key request. Waiting for reply. Requested at %1 Odosielam požiadavku na verejný kľúč. Čakám na odpoveď. Vyžiadaný %1 @@ -1314,37 +1343,37 @@ Priímcova požadovaná obtiažnosť: %1 a %2 Mapovanie portov UPnP zrušené - + Mark all messages as read Označiť všetky správy ako prečítané - + Are you sure you would like to mark all messages read? Ste si istý, že chcete označiť všetky správy ako prečítané? - + Doing work necessary to send broadcast. Vykonávam prácu potrebnú na rozoslanie. - + Proof of work pending Vykonávaná práca - + %n object(s) pending proof of work %n objekt čaká na vykonanie práce%n objekty čakajú na vykonanie práce%n objektov čaká na vykonanie práce - + %n object(s) waiting to be distributed %n objekt čaká na rozoslanie%n objekty čakajú na rozoslanie%n objektov čaká na rozoslanie - + Wait until these tasks finish? Počkať, kým tieto úlohy skončia? @@ -1404,12 +1433,17 @@ Priímcova požadovaná obtiažnosť: %1 a %2 Nie je rozumieť NMControl-u. - + Your GPU(s) did not calculate correctly, disabling OpenCL. Please report to the developers. Vaša grafická karta vykonala nesprávny výpočet, deaktivujem OpenCL. Prosím, pošlite správu vývojárom. - + + Set notification sound... + Nastaviť zvuk oznámenia... + + + Welcome to easy and secure Bitmessage * send messages to other people @@ -1424,105 +1458,140 @@ Vitajte v jednoduchom a bezpečnom Bitmessage - + not recommended for chans nie je odporúčaná pre kanály - + + Quiet Mode + Tichý režim + + + Problems connecting? Try enabling UPnP in the Network Settings Problémy so spojením? Skúste zapnúť UPnP v Nastaveniach siete - + + You are trying to send an email instead of a bitmessage. This requires registering with a gateway. Attempt to register? + Pokúšate sa odoslať e-mail namiesto bitmessage. To si vyžaduje registráciu na bráne. Pokúsiť sa o registráciu? + + + Error: Bitmessage addresses start with BM- Please check the recipient address %1 Chyba: Bitmessage adresy začínajú s BM- Prosím skontrolujte adresu príjemcu %1 - + Error: The recipient address %1 is not typed or copied correctly. Please check it. Chyba: adresa príjemcu %1 nie je na správne napísaná alebo skopírovaná. Prosím skontrolujte ju. - + Error: The recipient address %1 contains invalid characters. Please check it. Chyba: adresa príjemcu %1 obsahuje neplatné znaky. Prosím skontrolujte ju. - + Error: The version of the recipient address %1 is too high. Either you need to upgrade your Bitmessage software or your acquaintance is being clever. Chyba: verzia adresy príjemcu %1 je príliš veľká. Buď musíte aktualizovať program Bitmessage alebo váš známy s vami žartuje. - + Error: Some data encoded in the recipient address %1 is too short. There might be something wrong with the software of your acquaintance. Chyba: niektoré údaje zakódované v adrese príjemcu %1 sú príliš krátke. Softér vášho známeho možno nefunguje správne. - + Error: Some data encoded in the recipient address %1 is too long. There might be something wrong with the software of your acquaintance. Chyba: niektoré údaje zakódované v adrese príjemcu %1 sú príliš dlhé. Softvér vášho známeho možno nefunguje správne. - + Error: Some data encoded in the recipient address %1 is malformed. There might be something wrong with the software of your acquaintance. Chyba: niektoré údaje zakódované v adrese príjemcu %1 sú poškodené. Softvér vášho známeho možno nefunguje správne. - + Error: Something is wrong with the recipient address %1. Chyba: niečo s adresou príjemcu %1 je nie je v poriadku. - + + Error: %1 + Chyba: %1 + + + + From %1 + Od %1 + + + Synchronisation pending Prebieha synchronizácia - + Bitmessage hasn't synchronised with the network, %n object(s) to be downloaded. If you quit now, it may cause delivery delays. Wait until the synchronisation finishes? Bitmessage sa nezosynchronizoval so sieťou, treba prevzať ešte %n objekt. Keď program ukončíte teraz, môže dôjsť k spozdeniu doručenia. Počkať, kým sa synchronizácia ukončí?Bitmessage sa nezosynchronizoval so sieťou, treba prevzať ešte %n objekty. Keď program ukončíte teraz, môže dôjsť k spozdeniu doručenia. Počkať, kým sa synchronizácia ukončí?Bitmessage sa nezosynchronizoval so sieťou, treba prevzať ešte %n objektov. Keď program ukončíte teraz, môže dôjsť k spozdeniu doručenia. Počkať, kým sa synchronizácia ukončí? - + Not connected Nepripojený - + Bitmessage isn't connected to the network. If you quit now, it may cause delivery delays. Wait until connected and the synchronisation finishes? Bitmessage nie je pripojený do siete. Keď program ukončíte teraz, môže dôjsť k spozdeniu doručenia. Počkať, kým dôjde k spojeniu a prebehne synchronizácia? - + Waiting for network connection... Čakám na pripojenie do siete... - + Waiting for finishing synchronisation... Čakám na ukončenie synchronizácie... + + + You have already set a notification sound for this address book entry. Do you really want to overwrite it? + Pre túto adresu ste už ste nastavili zvuk oznámenia. Naozaj ho chcete ho zmeniť? + + + + Go online + Pripojiť k sieti + + + + Go offline + Odpojiť od siete + MessageView - + Follow external link Nasledovať externý odkaz - + The link "%1" will open in a browser. It may be a security risk, it could de-anonymise you or download malicious data. Are you sure? Odkaz "%1" bude otvorený v prehliadači. Tento úkon môže predstavovať bezpečnostné riziko a Vás deanonymizovať, alebo vykonať škodlivú činnost. Ste si istý? - + HTML detected, click here to display Nájdené HTML, kliknite sem ak chcete zobraziť - + Click here to disable HTML Kliknite sem na vypnutie HTML @@ -1545,99 +1614,99 @@ Možno by ste mali inovovať Bitmessage. NewAddressDialog - + Create new Address Vytvoriť novú adresu - + Here you may generate as many addresses as you like. Indeed, creating and abandoning addresses is encouraged. You may generate addresses by using either random numbers or by using a passphrase. If you use a passphrase, the address is called a "deterministic" address. The 'Random Number' option is selected by default but deterministic addresses have several pros and cons: Tu si môžete vygenerovať toľko adries, koľko chcete. Vytváranie a opúšťanie adries je vrelo odporúčané. Adresy môžete generovať buď pomocou náhodných čísel alebo pomocou hesla. Ak používate heslo, takáto adresa sa nazýva "deterministická". Predvoľba je pomocou generátora náhodných čísiel, ale deterministické adresy majú niekoľko výhod a nevýhod: - + <html><head/><body><p><span style=" font-weight:600;">Pros:<br/></span>You can recreate your addresses on any computer from memory. <br/>You need-not worry about backing up your keys.dat file as long as you can remember your passphrase. <br/><span style=" font-weight:600;">Cons:<br/></span>You must remember (or write down) your passphrase if you expect to be able to recreate your keys if they are lost. <br/>You must remember the address version number and the stream number along with your passphrase. <br/>If you choose a weak passphrase and someone on the Internet can brute-force it, they can read your messages and send messages as you.</p></body></html> <html><head/><body><p> <span style=" font-weight:600;">Pros: <br/></span>Svoje adresy môžete znovu vytvoriť na ľubovoľnom počítači z pamäte.<br/>Dokým si pamätáte heslo, nemusíte sa starať o zálohovanie keys.dat<br/> <span style=" font-weight:600;"Nevýhody: <br/></span>Ak chcete znovu vytvoriť kľúče ak ich stratíte, musíte si pamätať (alebo niekam zapísať) heslo. <br/> Zároveň si musíte si zapamätať aj číslo verzie adresy a číslo toku.<br/>Ak si zvolíte slabé prístupové heslo a niekto na internete ho uhádne, napr. hrubou silou, môže čítať vaše správy a odosielať ich za vás.</p></body></html> - + Use a random number generator to make an address Vytvoriť novú adresu pomocou generátora náhodných čísel - + Use a passphrase to make addresses Vytvoriť novú adresu pomocou hesla - + Spend several minutes of extra computing time to make the address(es) 1 or 2 characters shorter Stráviť niekoľko minút výpočtového času navyše, aby adresa(y) bola o 1 alebo 2 znakov kratšia - + Make deterministic addresses Vytvoriť deterministické adresy - + Address version number: 4 Číslo verzie adresy: 4 - + In addition to your passphrase, you must remember these numbers: Okrem svojho hesla si musíte zapamätať tiejto údaje: - + Passphrase Heslo - + Number of addresses to make based on your passphrase: Počet adries, ktoré majú byť na základe vášho hesla vytvorené: - + Stream number: 1 Číslo prúdu: 1 - + Retype passphrase Opakovať heslo - + Randomly generate address Adresu generovať náhodne - + Label (not shown to anyone except you) Označenie (zobrazené len vám a nikomu inému) - + Use the most available stream Použiť najviac prístupný prúd - + (best if this is the first of many addresses you will create) (najlepšie, ak ide o prvú z mnohých vytvorených adries) - + Use the same stream as an existing address Použiť ten istý prúd ako existujúca adresa - + (saves you some bandwidth and processing power) (ušetrí nejaké množstvo prenesených dát a výpočtový výkon) @@ -1645,22 +1714,22 @@ Predvoľba je pomocou generátora náhodných čísiel, ale deterministické adr NewSubscriptionDialog - + Add new entry Pridať nový záznam - + Label Označenie - + Address Adresa - + Enter an address above. Zadajte adresu vyššie. @@ -1668,55 +1737,60 @@ Predvoľba je pomocou generátora náhodných čísiel, ale deterministické adr SpecialAddressBehaviorDialog - + Special Address Behavior Zvláštne správanie adresy - + Behave as a normal address Správanie ako normálna adresa - + Behave as a pseudo-mailing-list address Správanie ako pseudo poštový zoznam - + Mail received to a pseudo-mailing-list address will be automatically broadcast to subscribers (and thus will be public). Správy prijaté na adresu pseudo poštového zoznamu budú automaticky rozoslaná odberateľom (a teda budú zverejnené). - + Name of the pseudo-mailing-list: Meno pseudo poštového zoznamu: + + + This is a chan address. You cannot use it as a pseudo-mailing list. + Toto je adresa kanálu. Nie je možné ju používať ako pseudo poštový zoznam. + aboutDialog - + About O - + PyBitmessage - PyBitmessage + - + version ? - verzia ? + - + <html><head/><body><p>Distributed under the MIT/X11 software license; see <a href="http://www.opensource.org/licenses/mit-license.php"><span style=" text-decoration: underline; color:#0000ff;">http://www.opensource.org/licenses/mit-license.php</span></a></p></body></html> <html><head/><body><p>Šírený pod licenciou na softvér MIT / X11; pozri <a href="http://www.opensource.org/licenses/mit-license.php"><span style=" text-decoration: underline; color:#0000ff;">http://www.opensource.org/licenses/mit-license.php</span></a></p></body></html> - + This is Beta software. Toto je beta softvér. @@ -1725,10 +1799,10 @@ Predvoľba je pomocou generátora náhodných čísiel, ale deterministické adr <html><head/><body><p>Copyright © 2012-2016 Jonathan Warren<br/>Copyright © 2013-2016 The Bitmessage Developers</p></body></html> - - - <html><head/><body><p>Copyright &copy; 2012-2016 Jonathan Warren<br/>Copyright &copy; 2013-2016 The Bitmessage Developers</p></body></html> - <html><head/><body><p>Copyright &copy; 2012-2016 Jonathan Warren<br/>Copyright &copy; 2013-2016 Vývojári Bitmessage</p></body></html> + + + <html><head/><body><p>Copyright © 2012-2016 Jonathan Warren<br/>Copyright © 2013-2017 The Bitmessage Developers</p></body></html> + <html><head/><body><p>Copyright © 2012-2016 Jonathan Warren<br/>Copyright © 2013-2017 Vývojári Bitmessage</p></body></html> @@ -1772,40 +1846,45 @@ Predvoľba je pomocou generátora náhodných čísiel, ale deterministické adr connectDialog - + Bitmessage Bitmessage - + Bitmessage won't connect to anyone until you let it. Bitmessage sa s nikým nespojí, dokým to nepovolíte. - + Connect now Spojiť teraz - + Let me configure special network settings first Najprv upraviť zvláštne sieťové nastavenia + + + Work offline + Zostať odpojený + helpDialog - + Help Pomoc - + <a href="https://bitmessage.org/wiki/PyBitmessage_Help">https://bitmessage.org/wiki/PyBitmessage_Help</a> <a href="https://bitmessage.org/wiki/PyBitmessage_Help">https://bitmessage.org/wiki/PyBitmessage_Help</a> - + As Bitmessage is a collaborative project, help can be found online in the Bitmessage Wiki: Keďže Bitmessage je projekt založený na spolupráci, pomoc možno nájsť na internete v Bitmessage Wiki: @@ -1813,30 +1892,35 @@ Predvoľba je pomocou generátora náhodných čísiel, ale deterministické adr iconGlossaryDialog - + Icon Glossary Legenda ikon - + You have no connections with other peers. Nemáte žiadne spojenia s partnerskými uzlami. - + You have made at least one connection to a peer using an outgoing connection but you have not yet received any incoming connections. Your firewall or home router probably isn't configured to forward incoming TCP connections to your computer. Bitmessage will work just fine but it would help the Bitmessage network if you allowed for incoming connections and will help you be a better-connected node. Vykonali ste aspoň jedno vychádzajúce spojenie do siete, ale ešte ste nenaviazali žiadne prichádzajúce spojenia. Váš firewall alebo domáci router pravdepodobne nie je nakonfigurovaný tak, aby presmeroval prichádzajúce TCP spojenia k vášmu počítaču. Bitmessage bude fungovať v pohode, keby ste však mali fungujúce prichádzajúce spojenia, pomôžete sieti Bitmessage a váš uzol bude lepšie pripojený. You are using TCP port ?. (This can be changed in the settings). - Používate port TCP ?. (Možno zmeniť v nastaveniach). + - + You do have connections with other peers and your firewall is correctly configured. Máte spojenia s partnerskými uzlami a vaša brána firewall je nastavená správne. + + + You are using TCP port %1. (This can be changed in the settings). + Používate port TCP %1. (Možno zmeniť v nastaveniach). + networkstatus @@ -1846,37 +1930,37 @@ Predvoľba je pomocou generátora náhodných čísiel, ale deterministické adr Spojení spolu: - + Since startup: Od spustenia: - + Processed 0 person-to-person messages. Spracovaných 0 bežných správ. - + Processed 0 public keys. Spracovaných 0 verejných kľúčov. - + Processed 0 broadcasts. Spracovaných 0 hromadných správ. - + Inventory lookups per second: 0 Vyhľadaní v inventári za sekundu: 0 - + Objects to be synced: Zostáva synchronizovať objektov: - + Stream # Prúd # @@ -1886,112 +1970,112 @@ Predvoľba je pomocou generátora náhodných čísiel, ale deterministické adr - + Since startup on %1 Od spustenia %1 - + Down: %1/s Total: %2 Prijatých: %1/s Spolu: %2 - + Up: %1/s Total: %2 Odoslaných: %1/s Spolu: %2 - + Total Connections: %1 Spojení spolu: %1 - + Inventory lookups per second: %1 Vyhľadaní v inventári za sekundu: %1 - + Up: 0 kB/s Odoslaných: 0 kB/s - + Down: 0 kB/s Prijatých: 0 kB/s - + Network Status Stav siete - + byte(s) bajtbajtybajtov - + Object(s) to be synced: %n Zostáva synchronizovať %n objektZostáva synchronizovať %n objektyZostáva synchronizovať %n objektov - + Processed %n person-to-person message(s). Spracovaná %n bežná správa.Spracované %n bežné správy.Spracovaných %n bežných správ. - + Processed %n broadcast message(s). Spracovaná %n hromadná správa.Spracované %n hromadné správy.Spracovaných %n hromadných správ. - + Processed %n public key(s). Spracovaný %n verejný kľúč.Spracované %n verejné kľúče.Spracovaných %n verejných kľúčov. - + Peer - partnerský uzol + Partnerský uzol - + IP address or hostname IP adresa alebo názov hostiteľa - + Rating Hodnotenie - + PyBitmessage tracks the success rate of connection attempts to individual nodes. The rating ranges from -1 to 1 and affects the likelihood of selecting the node in the future PyBitmessage sleduje úspešnosť pokusov o pripojenie k jednotlivým uzlom. Hodnotenie sa pohybuje od -1 do 1 a ovplyvňuje pravdepodobnosť výberu uzla v budúcnosti - + User agent Užívateľský agent - + Peer's self-reported software Software hlásený partnerským uzlom - + TLS TLS - + Connection encryption Šifrovanie pripojenia - + List of streams negotiated between you and the peer Zoznam prúdov dohodnutých medzi vami a partnerom @@ -2050,7 +2134,7 @@ Predvoľba je pomocou generátora náhodných čísiel, ale deterministické adr - Chan passhphrase/name: + Chan passphrase/name: Názov kanálu: @@ -2072,7 +2156,7 @@ Predvoľba je pomocou generátora náhodných čísiel, ale deterministické adr newchandialog - + Successfully created / joined chan %1 Kanál %1 úspešne vytvorený/pripojený @@ -2090,17 +2174,17 @@ Predvoľba je pomocou generátora náhodných čísiel, ale deterministické adr proofofwork - + C PoW module built successfully. C PoW modul úspešne zostavený. - + Failed to build C PoW module. Please build it manually. Zostavenie C PoW modulu zlyhalo. Prosím, zostavte ho manuálne. - + C PoW module unavailable. Please build it. C PoW modul nie je dostupný. Prosím, zostavte ho. @@ -2116,54 +2200,54 @@ Predvoľba je pomocou generátora náhodných čísiel, ale deterministické adr regenerateAddressesDialog - + Regenerate Existing Addresses Znova vytvoriť existujúce adresy - + Regenerate existing addresses Znova vytvoriť existujúce adresy - + Passphrase Heslo - + Number of addresses to make based on your passphrase: Počet adries, ktoré majú byť na základe vášho hesla vytvorené: - + Address version number: Číslo verzie adresy: - + Stream number: Číslo prúdu: - + 1 1 - + Spend several minutes of extra computing time to make the address(es) 1 or 2 characters shorter Stráviť niekoľko minút výpočtového času navyše, aby adresa(y) bola o 1 alebo 2 znakov kratšia - + You must check (or not check) this box just like you did (or didn't) when you made your addresses the first time. - Je nutné začiarknuť (alebo nezačiarknuť) toto políčko tak isto, ako keď ste vytvárali svoje adresy prvýkrát. + - + If you have previously made deterministic addresses but lost them due to an accident (like hard drive failure), you can regenerate them here. If you used the random number generator to make your addresses then this form will be of no use to you. - Ak ste v minulosti používali deterministické adresy, ale stratili ich kvôli nehode (ako je napráklad zlyhanie pevného disku), môžete ich vytvoriť znova. Ak ste na vytvorenie adries použili generátor náhodných čísel, potom je vám tento formulár zbytočný. + From fd1a6c1fa14ab719f43d97ad52f464826fb32b4c Mon Sep 17 00:00:00 2001 From: Peter Surda Date: Sat, 3 Feb 2018 11:46:39 +0100 Subject: [PATCH 378/407] Dandelion update - dandelion fixes - try to wait as long as possible before expiration if there are no outbound connections - expire in invThread rather than singleCleaner thread - deduplication of code in inv and dinv command methods - turn on by default, seems to work correctly now - turn off dandelion if outbound connections are disabled - start tracking downloads earlier, and faster download loop - remove some obsolete lines - minor PEP8 updates --- src/bitmessagemain.py | 5 ++ src/bmconfigparser.py | 2 +- src/class_singleCleaner.py | 3 -- src/network/bmobject.py | 2 +- src/network/bmproto.py | 43 +++++++-------- src/network/connectionpool.py | 51 +++++------------- src/network/dandelion.py | 98 ++++++++++++++++++++--------------- src/network/downloadthread.py | 5 +- src/network/invthread.py | 33 ++++++------ src/network/objectracker.py | 37 +++++++++---- src/network/tcp.py | 2 +- src/protocol.py | 4 +- src/state.py | 2 + 13 files changed, 149 insertions(+), 138 deletions(-) diff --git a/src/bitmessagemain.py b/src/bitmessagemain.py index 74011f29..accd5740 100755 --- a/src/bitmessagemain.py +++ b/src/bitmessagemain.py @@ -232,6 +232,11 @@ class Main: helper_threading.set_thread_name("PyBitmessage") + state.dandelion = BMConfigParser().safeGetInt('network', 'dandelion') + # dandelion requires outbound connections, without them, stem objects will get stuck forever + if state.dandelion and not BMConfigParser().safeGetBoolean('bitmessagesettings', 'sendoutgoingconnections'): + state.dandelion = 0 + helper_bootstrap.knownNodes() # Start the address generation thread addressGeneratorThread = addressGenerator() diff --git a/src/bmconfigparser.py b/src/bmconfigparser.py index bb4377a2..6a598955 100644 --- a/src/bmconfigparser.py +++ b/src/bmconfigparser.py @@ -20,7 +20,7 @@ BMConfigDefaults = { }, "network": { "bind": '', - "dandelion": 0, + "dandelion": 90, }, "inventory": { "storage": "sqlite", diff --git a/src/class_singleCleaner.py b/src/class_singleCleaner.py index 1def6cdb..ca77881c 100644 --- a/src/class_singleCleaner.py +++ b/src/class_singleCleaner.py @@ -10,7 +10,6 @@ from helper_sql import * from helper_threading import * from inventory import Inventory from network.connectionpool import BMConnectionPool -from network.dandelion import Dandelion from debug import logger import knownnodes import queues @@ -133,8 +132,6 @@ class singleCleaner(threading.Thread, StoppableThread): # inv/object tracking for connection in BMConnectionPool().inboundConnections.values() + BMConnectionPool().outboundConnections.values(): connection.clean() - # dandelion fluff trigger by expiration - Dandelion().expire() # discovery tracking exp = time.time() - singleCleaner.expireDiscoveredPeers diff --git a/src/network/bmobject.py b/src/network/bmobject.py index 267cac58..2e7dd092 100644 --- a/src/network/bmobject.py +++ b/src/network/bmobject.py @@ -74,7 +74,7 @@ class BMObject(object): def checkAlreadyHave(self): # if it's a stem duplicate, pretend we don't have it - if self.inventoryHash in Dandelion().hashMap: + if Dandelion().hasHash(self.inventoryHash): return if self.inventoryHash in Inventory(): raise BMObjectAlreadyHaveError() diff --git a/src/network/bmproto.py b/src/network/bmproto.py index 4bf63bca..28277f52 100644 --- a/src/network/bmproto.py +++ b/src/network/bmproto.py @@ -66,8 +66,6 @@ class BMProto(AdvancedDispatcher, ObjectTracker): self.payloadOffset = 0 self.expectBytes = protocol.Header.size self.object = None - self.dandelionRoutes = [] - self.dandelionRefresh = 0 def state_bm_header(self): self.magic, self.command, self.payloadLength, self.checksum = protocol.Header.unpack(self.read_buf[:protocol.Header.size]) @@ -282,8 +280,8 @@ class BMProto(AdvancedDispatcher, ObjectTracker): #TODO make this more asynchronous random.shuffle(items) for i in map(str, items): - if i in Dandelion().hashMap and \ - self != Dandelion().hashMap[i]: + if Dandelion().hasHash(i) and \ + self != Dandelion().objectChildStem(i): self.antiIntersectionDelay() logger.info('%s asked for a stem object we didn\'t offer to it.', self.destination) break @@ -298,41 +296,36 @@ class BMProto(AdvancedDispatcher, ObjectTracker): # when using random reordering, as the recipient won't know exactly which objects we refuse to deliver return True - def bm_command_inv(self): + def _command_inv(self, dandelion=False): items = self.decode_payload_content("l32s") if len(items) >= BMProto.maxObjectCount: - logger.error("Too many items in inv message!") + logger.error("Too many items in %sinv message!", "d" if dandelion else "") raise BMProtoExcessiveDataError() else: pass + # ignore dinv if dandelion turned off + if dandelion and not state.dandelion: + return True + for i in map(str, items): + if i in Inventory() and not Dandelion().hasHash(i): + continue + if dandelion and not Dandelion().hasHash(i): + Dandelion().addHash(i, self) self.handleReceivedInventory(i) return True + def bm_command_inv(self): + return self._command_inv(False) + def bm_command_dinv(self): """ Dandelion stem announce """ - items = self.decode_payload_content("l32s") - - if len(items) >= BMProto.maxObjectCount: - logger.error("Too many items in dinv message!") - raise BMProtoExcessiveDataError() - else: - pass - - # ignore command if dandelion turned off - if BMConfigParser().safeGetBoolean("network", "dandelion") == 0: - return True - - for i in map(str, items): - self.handleReceivedInventory(i) - Dandelion().addHash(i, self) - - return True + return self._command_inv(True) def bm_command_object(self): objectOffset = self.payloadOffset @@ -368,8 +361,12 @@ class BMProto(AdvancedDispatcher, ObjectTracker): except KeyError: pass + if self.object.inventoryHash in Inventory() and Dandelion().hasHash(self.object.inventoryHash): + Dandelion().removeHash(self.object.inventoryHash, "cycle detection") + Inventory()[self.object.inventoryHash] = ( self.object.objectType, self.object.streamNumber, buffer(self.payload[objectOffset:]), self.object.expiresTime, buffer(self.object.tag)) + self.handleReceivedObject(self.object.streamNumber, self.object.inventoryHash) invQueue.put((self.object.streamNumber, self.object.inventoryHash, self.destination)) return True diff --git a/src/network/connectionpool.py b/src/network/connectionpool.py index 2f34e485..2c3b5054 100644 --- a/src/network/connectionpool.py +++ b/src/network/connectionpool.py @@ -8,10 +8,10 @@ from bmconfigparser import BMConfigParser from debug import logger import helper_bootstrap from network.proxy import Proxy -import network.bmproto +from network.bmproto import BMProto from network.dandelion import Dandelion -import network.tcp -import network.udp +from network.tcp import TCPServer, Socks5BMConnection, Socks4aBMConnection, TCPConnection +from network.udp import UDPSocket from network.connectionchooser import chooseConnection import network.asyncore_pollchoose as asyncore import protocol @@ -33,31 +33,6 @@ class BMConnectionPool(object): self.spawnWait = 2 self.bootstrapped = False - def handleReceivedObject(self, streamNumber, hashid, connection = None): - for i in self.inboundConnections.values() + self.outboundConnections.values(): - if not isinstance(i, network.bmproto.BMProto): - continue - if not i.fullyEstablished: - continue - try: - del i.objectsNewToMe[hashid] - except KeyError: - with i.objectsNewToThemLock: - i.objectsNewToThem[hashid] = time.time() - if i == connection: - try: - with i.objectsNewToThemLock: - del i.objectsNewToThem[hashid] - except KeyError: - pass - if hashid in Dandelion().fluff: - Dandelion().removeHash(hashid) - - def reRandomiseDandelionStems(self): - # Choose 2 peers randomly - # TODO: handle streams - Dandelion().reRandomiseStems(self.outboundConnections.values()) - def connectToStream(self, streamNumber): self.streams.append(streamNumber) @@ -88,7 +63,7 @@ class BMConnectionPool(object): return False def addConnection(self, connection): - if isinstance(connection, network.udp.UDPSocket): + if isinstance(connection, UDPSocket): return if connection.isOutbound: self.outboundConnections[connection.destination] = connection @@ -99,9 +74,9 @@ class BMConnectionPool(object): self.inboundConnections[connection.destination.host] = connection def removeConnection(self, connection): - if isinstance(connection, network.udp.UDPSocket): + if isinstance(connection, UDPSocket): del self.udpSockets[connection.listening.host] - elif isinstance(connection, network.tcp.TCPServer): + elif isinstance(connection, TCPServer): del self.listeningSockets[state.Peer(connection.destination.host, connection.destination.port)] elif connection.isOutbound: try: @@ -135,18 +110,18 @@ class BMConnectionPool(object): bind = self.getListeningIP() port = BMConfigParser().safeGetInt("bitmessagesettings", "port") # correct port even if it changed - ls = network.tcp.TCPServer(host=bind, port=port) + ls = TCPServer(host=bind, port=port) self.listeningSockets[ls.destination] = ls def startUDPSocket(self, bind=None): if bind is None: host = self.getListeningIP() - udpSocket = network.udp.UDPSocket(host=host, announcing=True) + udpSocket = UDPSocket(host=host, announcing=True) else: if bind is False: - udpSocket = network.udp.UDPSocket(announcing=False) + udpSocket = UDPSocket(announcing=False) else: - udpSocket = network.udp.UDPSocket(host=bind, announcing=True) + udpSocket = UDPSocket(host=bind, announcing=True) self.udpSockets[udpSocket.listening.host] = udpSocket def loop(self): @@ -192,11 +167,11 @@ class BMConnectionPool(object): # continue try: if (BMConfigParser().safeGet("bitmessagesettings", "socksproxytype") == "SOCKS5"): - self.addConnection(network.tcp.Socks5BMConnection(chosen)) + self.addConnection(Socks5BMConnection(chosen)) elif (BMConfigParser().safeGet("bitmessagesettings", "socksproxytype") == "SOCKS4a"): - self.addConnection(network.tcp.Socks4aBMConnection(chosen)) + self.addConnection(Socks4aBMConnection(chosen)) elif not chosen.host.endswith(".onion"): - self.addConnection(network.tcp.TCPConnection(chosen)) + self.addConnection(TCPConnection(chosen)) except socket.error as e: if e.errno == errno.ENETUNREACH: continue diff --git a/src/network/dandelion.py b/src/network/dandelion.py index 3e49d906..edd4fb5b 100644 --- a/src/network/dandelion.py +++ b/src/network/dandelion.py @@ -1,62 +1,78 @@ -from random import choice, shuffle +from collections import namedtuple +from random import choice, sample from threading import RLock from time import time from bmconfigparser import BMConfigParser +import network.connectionpool +from debug import logging +from queues import invQueue from singleton import Singleton +import state # randomise routes after 600 seconds REASSIGN_INTERVAL = 600 -FLUFF_TRIGGER_TIMEOUT = 300 +# trigger fluff due to expiration in 2 minutes +FLUFF_TRIGGER_TIMEOUT = 120 MAX_STEMS = 2 +Stem = namedtuple('Stem', ['child', 'stream', 'timeout']) + @Singleton class Dandelion(): def __init__(self): + # currently assignable child stems self.stem = [] + # currently assigned parent <-> child mappings self.nodeMap = {} + # currently existing objects in stem mode self.hashMap = {} - self.fluff = {} - self.timeout = {} + # when to rerandomise routes self.refresh = time() + REASSIGN_INTERVAL self.lock = RLock() - def addHash(self, hashId, source): - if BMConfigParser().safeGetInt('network', 'dandelion') == 0: + def addHash(self, hashId, source=None, stream=1): + if not state.dandelion: return with self.lock: - self.hashMap[hashId] = self.getNodeStem(source) - self.timeout[hashId] = time() + FLUFF_TRIGGER_TIMEOUT + self.hashMap[hashId] = Stem( + self.getNodeStem(source), + stream, + time() + FLUFF_TRIGGER_TIMEOUT) - def removeHash(self, hashId): + def setHashStream(self, hashId, stream=1): + with self.lock: + if hashId in self.hashMap: + self.hashMap[hashId] = Stem( + self.hashMap[hashId].child, + stream, + time() + FLUFF_TRIGGER_TIMEOUT) + + def removeHash(self, hashId, reason="no reason specified"): + logging.debug("%s entering fluff mode due to %s.", ''.join('%02x'%ord(i) for i in hashId), reason) with self.lock: try: del self.hashMap[hashId] except KeyError: pass - try: - del self.timeout[hashId] - except KeyError: - pass - try: - del self.fluff[hashId] - except KeyError: - pass - def fluffTrigger(self, hashId): - with self.lock: - self.fluff[hashId] = None + def hasHash(self, hashId): + return hashId in self.hashMap + + def objectChildStem(self, hashId): + return self.hashMap[hashId].child def maybeAddStem(self, connection): # fewer than MAX_STEMS outbound connections at last reshuffle? with self.lock: if len(self.stem) < MAX_STEMS: self.stem.append(connection) - # active mappings pointing nowhere - for k in (k for k, v in self.nodeMap.iteritems() if self.nodeMap[k] is None): + for k in (k for k, v in self.nodeMap.iteritems() if v is None): self.nodeMap[k] = connection - for k in (k for k, v in self.hashMap.iteritems() if self.hashMap[k] is None): - self.hashMap[k] = connection + for k, v in {k: v for k, v in self.hashMap.iteritems() if v.child is None}.iteritems(): + self.hashMap[k] = Stem(connection, v.stream, time() + FLUFF_TRIGGER_TIMEOUT) + invQueue.put((v.stream, k, v.child)) + def maybeRemoveStem(self, connection): # is the stem active? @@ -64,12 +80,10 @@ class Dandelion(): if connection in self.stem: self.stem.remove(connection) # active mappings to pointing to the removed node - for k in (k for k, v in self.nodeMap.iteritems() if self.nodeMap[k] == connection): + for k in (k for k, v in self.nodeMap.iteritems() if v == connection): self.nodeMap[k] = None - for k in (k for k, v in self.hashMap.iteritems() if self.hashMap[k] == connection): - self.hashMap[k] = None - if len(self.stem) < MAX_STEMS: - self.stem.append(connection) + for k, v in {k: v for k, v in self.hashMap.iteritems() if v.child == connection}.iteritems(): + self.hashMap[k] = Stem(None, v.stream, time() + FLUFF_TRIGGER_TIMEOUT) def pickStem(self, parent=None): try: @@ -92,26 +106,26 @@ class Dandelion(): try: return self.nodeMap[node] except KeyError: - self.nodeMap[node] = self.pickStem() + self.nodeMap[node] = self.pickStem(node) return self.nodeMap[node] - def getHashStem(self, hashId): - with self.lock: - return self.hashMap[hashId] - def expire(self): with self.lock: deadline = time() - toDelete = [k for k, v in self.hashMap.iteritems() if self.timeout[k] < deadline] - for k in toDelete: - del self.timeout[k] - del self.hashMap[k] + # only expire those that have a child node, i.e. those without a child not will stick around + toDelete = [[v.stream, k, v.child] for k, v in self.hashMap.iteritems() if v.timeout < deadline and v.child] + for row in toDelete: + self.removeHash(row[1], 'expiration') + invQueue.put((row[0], row[1], row[2])) - def reRandomiseStems(self, connections): - shuffle(connections) + def reRandomiseStems(self): with self.lock: - # random two connections - self.stem = connections[:MAX_STEMS] + try: + # random two connections + self.stem = sample(network.connectionpool.BMConnectionPool().outboundConnections.values(), MAX_STEMS) + # not enough stems available + except ValueError: + self.stem = network.connectionpool.BMConnectionPool().outboundConnections.values() self.nodeMap = {} # hashMap stays to cater for pending stems self.refresh = time() + REASSIGN_INTERVAL diff --git a/src/network/downloadthread.py b/src/network/downloadthread.py index 88f7c12e..7eee2761 100644 --- a/src/network/downloadthread.py +++ b/src/network/downloadthread.py @@ -3,6 +3,7 @@ import threading import time import addresses +from dandelion import Dandelion from debug import logger from helper_threading import StoppableThread from inventory import Inventory @@ -54,7 +55,7 @@ class DownloadThread(threading.Thread, StoppableThread): payload = bytearray() payload.extend(addresses.encodeVarint(len(request))) for chunk in request: - if chunk in Inventory(): + if chunk in Inventory() and not Dandelion().hasHash(chunk): try: del i.objectsNewToMe[chunk] except KeyError: @@ -70,4 +71,4 @@ class DownloadThread(threading.Thread, StoppableThread): if time.time() >= self.lastCleaned + DownloadThread.cleanInterval: self.cleanPending() if not requested: - self.stop.wait(5) + self.stop.wait(1) diff --git a/src/network/invthread.py b/src/network/invthread.py index 5852df0b..d0d758fb 100644 --- a/src/network/invthread.py +++ b/src/network/invthread.py @@ -18,26 +18,28 @@ class InvThread(threading.Thread, StoppableThread): self.initStop() self.name = "InvBroadcaster" + def handleLocallyGenerated(self, stream, hashId): + Dandelion().addHash(hashId, stream=stream) + for connection in BMConnectionPool().inboundConnections.values() + \ + BMConnectionPool().outboundConnections.values(): + if state.dandelion and connection != Dandelion().objectChildStem(hashId): + continue + connection.objectsNewToThem[hashId] = time() + def run(self): while not state.shutdown: chunk = [] while True: + # Dandelion fluff trigger by expiration + Dandelion().expire() try: data = invQueue.get(False) chunk.append((data[0], data[1])) # locally generated - if len(data) == 2: - Dandelion().addHash(data[1], None) - BMConnectionPool().handleReceivedObject(data[0], data[1]) - # came over the network - else: - source = BMConnectionPool().getConnectionByAddr(data[2]) - BMConnectionPool().handleReceivedObject(data[0], data[1], source) + if len(data) == 2 or data[2] is None: + self.handleLocallyGenerated(data[0], data[1]) except Queue.Empty: break - # connection not found, handle it as if generated locally - except KeyError: - BMConnectionPool().handleReceivedObject(data[0], data[1]) if chunk: for connection in BMConnectionPool().inboundConnections.values() + \ @@ -53,12 +55,13 @@ class InvThread(threading.Thread, StoppableThread): except KeyError: continue try: - if connection == Dandelion().hashMap[inv[1]]: + if connection == Dandelion().objectChildStem(inv[1]): # Fluff trigger by RNG # auto-ignore if config set to 0, i.e. dandelion is off - # send a normal inv if stem node doesn't support dandelion - if randint(1, 100) < BMConfigParser().safeGetBoolean("network", "dandelion") and \ - connection.services | protocol.NODE_DANDELION > 0: + if randint(1, 100) >= state.dandelion: + fluffs.append(inv[1]) + # send a dinv only if the stem node supports dandelion + elif connection.services & protocol.NODE_DANDELION > 0: stems.append(inv[1]) else: fluffs.append(inv[1]) @@ -79,6 +82,6 @@ class InvThread(threading.Thread, StoppableThread): invQueue.task_done() if Dandelion().refresh < time(): - BMConnectionPool().reRandomiseDandelionStems() + Dandelion().reRandomiseStems() self.stop.wait(1) diff --git a/src/network/objectracker.py b/src/network/objectracker.py index 327304d9..66b0685b 100644 --- a/src/network/objectracker.py +++ b/src/network/objectracker.py @@ -1,9 +1,8 @@ -from Queue import Queue import time from threading import RLock -from debug import logger from inventory import Inventory +import network.connectionpool from network.dandelion import Dandelion from randomtrackingdict import RandomTrackingDict from state import missingObjects @@ -80,13 +79,32 @@ class ObjectTracker(object): del self.objectsNewToThem[hashId] except KeyError: pass - # Fluff trigger by cycle detection - if hashId not in Inventory() or hashId in Dandelion().hashMap: - if hashId in Dandelion().hashMap: - Dandelion().fluffTrigger(hashId) - if hashId not in missingObjects: - missingObjects[hashId] = time.time() - self.objectsNewToMe[hashId] = True + if hashId not in missingObjects: + missingObjects[hashId] = time.time() + self.objectsNewToMe[hashId] = True + + def handleReceivedObject(self, streamNumber, hashid): + for i in network.connectionpool.BMConnectionPool().inboundConnections.values() + network.connectionpool.BMConnectionPool().outboundConnections.values(): + if not i.fullyEstablished: + continue + try: + del i.objectsNewToMe[hashid] + except KeyError: + if streamNumber in i.streams and \ + (not Dandelion().hasHash(hashid) or \ + Dandelion().objectChildStem(hashid) == i): + with i.objectsNewToThemLock: + i.objectsNewToThem[hashid] = time.time() + # update stream number, which we didn't have when we just received the dinv + # also resets expiration of the stem mode + Dandelion().setHashStream(hashid, streamNumber) + + if i == self: + try: + with i.objectsNewToThemLock: + del i.objectsNewToThem[hashid] + except KeyError: + pass def hasAddr(self, addr): if haveBloom: @@ -109,4 +127,3 @@ class ObjectTracker(object): # tracking inv # - per node hash of items that neither the remote node nor we have # - diff --git a/src/network/tcp.py b/src/network/tcp.py index 5a27aca3..33c4b6ca 100644 --- a/src/network/tcp.py +++ b/src/network/tcp.py @@ -168,7 +168,7 @@ class TCPConnection(BMProto, TLSDispatcher): with self.objectsNewToThemLock: for objHash in Inventory().unexpired_hashes_by_stream(stream): # don't advertise stem objects on bigInv - if objHash in Dandelion().hashMap: + if Dandelion().hasHash(objHash): continue bigInvList[objHash] = 0 self.objectsNewToThem[objHash] = time.time() diff --git a/src/protocol.py b/src/protocol.py index ef31b6c1..e5b2c5c2 100644 --- a/src/protocol.py +++ b/src/protocol.py @@ -196,7 +196,7 @@ def assembleVersionMessage(remoteHost, remotePort, participatingStreams, server payload += pack('>q', NODE_NETWORK | (NODE_SSL if haveSSL(server) else 0) | - (NODE_DANDELION if BMConfigParser().safeGetInt('network', 'dandelion') > 0 else 0) + (NODE_DANDELION if state.dandelion else 0) ) payload += pack('>q', int(time.time())) @@ -213,7 +213,7 @@ def assembleVersionMessage(remoteHost, remotePort, participatingStreams, server payload += pack('>q', NODE_NETWORK | (NODE_SSL if haveSSL(server) else 0) | - (NODE_DANDELION if BMConfigParser().safeGetInt('network', 'dandelion') > 0 else 0) + (NODE_DANDELION if state.dandelion else 0) ) payload += '\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xFF\xFF' + pack( '>L', 2130706433) # = 127.0.0.1. This will be ignored by the remote host. The actual remote connected IP will be used. diff --git a/src/state.py b/src/state.py index 32433e2d..73e4f789 100644 --- a/src/state.py +++ b/src/state.py @@ -53,3 +53,5 @@ def resetNetworkProtocolAvailability(): networkProtocolAvailability = {'IPv4': None, 'IPv6': None, 'onion': None} resetNetworkProtocolAvailability() + +dandelion = 0 From a646ec49029f65608286cbf6295f1cf574d7e1d3 Mon Sep 17 00:00:00 2001 From: Peter Surda Date: Sun, 4 Feb 2018 21:03:54 +0100 Subject: [PATCH 379/407] Allow separate proxy for onions - new options in network section: onionsocksproxytype, onionsockshostname and onionsocksport. These allow to separate connectivity types for onion and non-onion addresses, e.g. connect to clear nodes over clearnet and onions over tor - also remove some obsolete imports --- src/network/connectionpool.py | 26 +++++++++++++++++++++----- src/network/proxy.py | 26 +++++++++++++++++++++++++- 2 files changed, 46 insertions(+), 6 deletions(-) diff --git a/src/network/connectionpool.py b/src/network/connectionpool.py index 2c3b5054..9b91386e 100644 --- a/src/network/connectionpool.py +++ b/src/network/connectionpool.py @@ -1,3 +1,4 @@ +from ConfigParser import NoOptionError, NoSectionError import errno import socket import time @@ -8,8 +9,6 @@ from bmconfigparser import BMConfigParser from debug import logger import helper_bootstrap from network.proxy import Proxy -from network.bmproto import BMProto -from network.dandelion import Dandelion from network.tcp import TCPServer, Socks5BMConnection, Socks4aBMConnection, TCPConnection from network.udp import UDPSocket from network.connectionchooser import chooseConnection @@ -143,6 +142,15 @@ class BMConnectionPool(object): self.bootstrapped = True Proxy.proxy = (BMConfigParser().safeGet("bitmessagesettings", "sockshostname"), BMConfigParser().safeGetInt("bitmessagesettings", "socksport")) + # TODO AUTH + # TODO reset based on GUI settings changes + try: + if not BMConfigParser().get("network", "onionsocksproxytype").beginswith("SOCKS"): + raise NoOptionError + Proxy.onionproxy = (BMConfigParser().get("network", "onionsockshostname"), + BMConfigParser().getint("network", "onionsocksport")) + except (NoOptionError, NoSectionError): + Proxy.onionproxy = None established = sum(1 for c in self.outboundConnections.values() if (c.connected and c.fullyEstablished)) pending = len(self.outboundConnections) - established if established < BMConfigParser().safeGetInt("bitmessagesettings", "maxoutboundconnections"): @@ -166,15 +174,23 @@ class BMConnectionPool(object): # if chosen.host == c.destination.host: # continue try: - if (BMConfigParser().safeGet("bitmessagesettings", "socksproxytype") == "SOCKS5"): + if chosen.host.endswith(".onion") and Proxy.onionproxy is not None: + if BMConfigParser().get("network", "onionsocksproxytype") == "SOCKS5": + self.addConnection(Socks5BMConnection(chosen)) + elif BMConfigParser().get("network", "onionsocksproxytype") == "SOCKS4a": + self.addConnection(Socks4aBMConnection(chosen)) + elif BMConfigParser().safeGet("bitmessagesettings", "socksproxytype") == "SOCKS5": self.addConnection(Socks5BMConnection(chosen)) - elif (BMConfigParser().safeGet("bitmessagesettings", "socksproxytype") == "SOCKS4a"): + elif BMConfigParser().safeGet("bitmessagesettings", "socksproxytype") == "SOCKS4a": self.addConnection(Socks4aBMConnection(chosen)) - elif not chosen.host.endswith(".onion"): + else: self.addConnection(TCPConnection(chosen)) except socket.error as e: if e.errno == errno.ENETUNREACH: continue + except (NoSectionError, NoOptionError): + # shouldn't happen + pass self.lastSpawned = time.time() else: diff --git a/src/network/proxy.py b/src/network/proxy.py index 96930c18..43298f63 100644 --- a/src/network/proxy.py +++ b/src/network/proxy.py @@ -37,6 +37,8 @@ class Proxy(AdvancedDispatcher): # instances should change too _proxy = ("127.0.0.1", 9050) _auth = None + _onion_proxy = None + _onion_auth = None _remote_dns = True @property @@ -58,6 +60,25 @@ class Proxy(AdvancedDispatcher): def auth(self, authTuple): self.__class__._auth = authTuple + @property + def onion_proxy(self): + return self.__class__._onion_proxy + + @onion_proxy.setter + def onion_proxy(self, address): + if address is not None and (not isinstance(address, tuple) or (len(address) < 2) or \ + (not isinstance(address[0], str) or not isinstance(address[1], int))): + raise ValueError + self.__class__._onion_proxy = address + + @property + def onion_auth(self): + return self.__class__._onion_auth + + @onion_auth.setter + def onion_auth(self, authTuple): + self.__class__._onion_auth = authTuple + def __init__(self, address): if not isinstance(address, state.Peer): raise ValueError @@ -66,7 +87,10 @@ class Proxy(AdvancedDispatcher): self.isOutbound = True self.fullyEstablished = False self.create_socket(socket.AF_INET, socket.SOCK_STREAM) - self.connect(self.proxy) + if address.host.endswith(".onion") and self.onion_proxy is not None: + self.connect(self.onion_proxy) + else: + self.connect(self.proxy) def handle_connect(self): self.set_state("init") From 03f08b3cfdaafd1ee9eeda286fc7bbe8c828c820 Mon Sep 17 00:00:00 2001 From: Peter Surda Date: Sun, 4 Feb 2018 21:16:30 +0100 Subject: [PATCH 380/407] Typo --- src/network/connectionpool.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/network/connectionpool.py b/src/network/connectionpool.py index 9b91386e..408d56e0 100644 --- a/src/network/connectionpool.py +++ b/src/network/connectionpool.py @@ -145,7 +145,7 @@ class BMConnectionPool(object): # TODO AUTH # TODO reset based on GUI settings changes try: - if not BMConfigParser().get("network", "onionsocksproxytype").beginswith("SOCKS"): + if not BMConfigParser().get("network", "onionsocksproxytype").startswith("SOCKS"): raise NoOptionError Proxy.onionproxy = (BMConfigParser().get("network", "onionsockshostname"), BMConfigParser().getint("network", "onionsocksport")) From 85cce42de08034b426954b02b516d817f35e1707 Mon Sep 17 00:00:00 2001 From: Dmitri Bogomolov <4glitch@gmail.com> Date: Mon, 5 Feb 2018 14:39:32 +0200 Subject: [PATCH 381/407] Update "Network Status" information only when the tab selected --- src/bitmessageqt/__init__.py | 10 ++++++++++ src/bitmessageqt/networkstatus.py | 14 +++++++++++--- 2 files changed, 21 insertions(+), 3 deletions(-) diff --git a/src/bitmessageqt/__init__.py b/src/bitmessageqt/__init__.py index 3422664b..f370f3b8 100644 --- a/src/bitmessageqt/__init__.py +++ b/src/bitmessageqt/__init__.py @@ -686,6 +686,10 @@ class MyForm(settingsmixin.SMainWindow): "itemSelectionChanged ()"), self.treeWidgetItemClicked) QtCore.QObject.connect(self.ui.treeWidgetChans, QtCore.SIGNAL( "itemChanged (QTreeWidgetItem *, int)"), self.treeWidgetItemChanged) + QtCore.QObject.connect( + self.ui.tabWidget, QtCore.SIGNAL("currentChanged(int)"), + self.tabWidgetCurrentChanged + ) # Put the colored icon on the status bar # self.pushButtonStatusIcon.setIcon(QIcon(":/newPrefix/images/yellowicon.png")) @@ -4012,6 +4016,12 @@ class MyForm(settingsmixin.SMainWindow): completerList[i] = item.label + " <" + item.address + ">" self.ui.lineEditTo.completer().model().setStringList(completerList) + def tabWidgetCurrentChanged(self, n): + if n == self.ui.tabWidget.indexOf(self.ui.networkstatus): + self.ui.networkstatus.startUpdate() + else: + self.ui.networkstatus.stopUpdate() + def writeNewAddressToTable(self, label, address, streamNumber): self.rerenderTabTreeMessages() self.rerenderTabTreeSubscriptions() diff --git a/src/bitmessageqt/networkstatus.py b/src/bitmessageqt/networkstatus.py index 67284495..02c0deaf 100644 --- a/src/bitmessageqt/networkstatus.py +++ b/src/bitmessageqt/networkstatus.py @@ -39,10 +39,18 @@ class NetworkStatus(QtGui.QWidget, RetranslateMixin): "updateNumberOfBroadcastsProcessed()"), self.updateNumberOfBroadcastsProcessed) QtCore.QObject.connect(self.UISignalThread, QtCore.SIGNAL( "updateNetworkStatusTab(PyQt_PyObject,PyQt_PyObject,PyQt_PyObject)"), self.updateNetworkStatusTab) - + self.timer = QtCore.QTimer() - self.timer.start(2000) # milliseconds - QtCore.QObject.connect(self.timer, QtCore.SIGNAL("timeout()"), self.runEveryTwoSeconds) + + QtCore.QObject.connect( + self.timer, QtCore.SIGNAL("timeout()"), self.runEveryTwoSeconds) + + def startUpdate(self): + self.runEveryTwoSeconds() + self.timer.start(2000) # milliseconds + + def stopUpdate(self): + self.timer.stop() def formatBytes(self, num): for x in [_translate("networkstatus", "byte(s)", None, QtCore.QCoreApplication.CodecForTr, num), "kB", "MB", "GB"]: From 4a3f8f4806c38d404053617db2071d69ad367dda Mon Sep 17 00:00:00 2001 From: Dmitri Bogomolov <4glitch@gmail.com> Date: Tue, 6 Feb 2018 15:48:56 +0200 Subject: [PATCH 382/407] Change unread status of message in "All Accounts" when it changed in "Chans" and vice versa (#1044, #884) --- src/bitmessageqt/__init__.py | 158 ++++++++++++++++++++--------------- 1 file changed, 89 insertions(+), 69 deletions(-) diff --git a/src/bitmessageqt/__init__.py b/src/bitmessageqt/__init__.py index f370f3b8..76940ec0 100644 --- a/src/bitmessageqt/__init__.py +++ b/src/bitmessageqt/__init__.py @@ -911,6 +911,46 @@ class MyForm(settingsmixin.SMainWindow): self.ui.tabWidget.indexOf(self.ui.chans) ) + def updateUnreadStatus(self, widget, row, msgid, unread=True): + """ + Switch unread for item of msgid and related items in + other STableWidgets "All Accounts" and "Chans" + """ + related = [self.ui.tableWidgetInbox, self.ui.tableWidgetInboxChans] + try: + related.remove(widget) + related = related.pop() + except ValueError: + rrow = None + related = [] + else: + # maybe use instead: + # rrow = related.row(msgid), msgid should be QTableWidgetItem + # related = related.findItems(msgid, QtCore.Qt.MatchExactly), + # returns an empty list + for rrow in xrange(related.rowCount()): + if msgid == str(related.item(rrow, 3).data( + QtCore.Qt.UserRole).toPyObject()): + break + else: + rrow = None + + status = widget.item(row, 0).unread + if status == unread: + font = QtGui.QFont() + font.setBold(not status) + widget.item(row, 3).setFont(font) + for col in (0, 1, 2): + widget.item(row, col).setUnread(not status) + + try: + related.item(rrow, 3).setFont(font) + except (TypeError, AttributeError): + pass + else: + for col in (0, 1, 2): + related.item(rrow, col).setUnread(not status) + def propagateUnreadCount(self, address = None, folder = "inbox", widget = None, type = 1): widgets = [self.ui.treeWidgetYourIdentities, self.ui.treeWidgetSubscriptions, self.ui.treeWidgetChans] queryReturn = sqlQuery("SELECT toaddress, folder, COUNT(msgid) AS cnt FROM inbox WHERE read = 0 GROUP BY toaddress, folder") @@ -2583,7 +2623,7 @@ class MyForm(settingsmixin.SMainWindow): ), QtGui.QMessageBox.Yes | QtGui.QMessageBox.No ) != QtGui.QMessageBox.Yes: return - addressAtCurrentRow = self.getCurrentAccount() + # addressAtCurrentRow = self.getCurrentAccount() tableWidget = self.getCurrentMessagelist() idCount = tableWidget.rowCount() @@ -2610,8 +2650,8 @@ class MyForm(settingsmixin.SMainWindow): ) if markread > 0: - self.propagateUnreadCount( - addressAtCurrentRow, self.getCurrentFolder(), None, 0) + self.propagateUnreadCount() + # addressAtCurrentRow, self.getCurrentFolder(), None, 0) def click_NewAddressDialog(self): dialogs.NewAddressDialog(self) @@ -2844,36 +2884,32 @@ class MyForm(settingsmixin.SMainWindow): tableWidget = self.getCurrentMessagelist() if not tableWidget: return - font = QtGui.QFont() - font.setBold(True) - inventoryHashesToMarkUnread = [] - modified = 0 + + msgids = set() + # modified = 0 for row in tableWidget.selectedIndexes(): currentRow = row.row() - inventoryHashToMarkUnread = str(tableWidget.item( + msgid = str(tableWidget.item( currentRow, 3).data(QtCore.Qt.UserRole).toPyObject()) - if inventoryHashToMarkUnread in inventoryHashesToMarkUnread: - # it returns columns as separate items, so we skip dupes - continue - if not tableWidget.item(currentRow, 0).unread: - modified += 1 - inventoryHashesToMarkUnread.append(inventoryHashToMarkUnread) - tableWidget.item(currentRow, 0).setUnread(True) - tableWidget.item(currentRow, 1).setUnread(True) - tableWidget.item(currentRow, 2).setUnread(True) - tableWidget.item(currentRow, 3).setFont(font) + msgids.add(msgid) + # if not tableWidget.item(currentRow, 0).unread: + # modified += 1 + self.updateUnreadStatus(tableWidget, currentRow, msgid, False) + # for 1081 - idCount = len(inventoryHashesToMarkUnread) - rowcount = sqlExecuteChunked( + idCount = len(msgids) + # rowcount = + sqlExecuteChunked( '''UPDATE inbox SET read=0 WHERE msgid IN ({0}) AND read=1''', - idCount, *inventoryHashesToMarkUnread + idCount, *msgids ) - if rowcount == 1: - # performance optimisation - self.propagateUnreadCount(tableWidget.item(currentRow, 1 if tableWidget.item(currentRow, 1).type == AccountMixin.SUBSCRIPTION else 0).data(QtCore.Qt.UserRole), self.getCurrentFolder()) - else: - self.propagateUnreadCount(tableWidget.item(currentRow, 1 if tableWidget.item(currentRow, 1).type == AccountMixin.SUBSCRIPTION else 0).data(QtCore.Qt.UserRole), self.getCurrentFolder(), self.getCurrentTreeWidget(), 0) + self.propagateUnreadCount() + # if rowcount == 1: + # # performance optimisation + # self.propagateUnreadCount(tableWidget.item(currentRow, 1 if tableWidget.item(currentRow, 1).type == AccountMixin.SUBSCRIPTION else 0).data(QtCore.Qt.UserRole), self.getCurrentFolder()) + # else: + # self.propagateUnreadCount(tableWidget.item(currentRow, 1 if tableWidget.item(currentRow, 1).type == AccountMixin.SUBSCRIPTION else 0).data(QtCore.Qt.UserRole), self.getCurrentFolder(), self.getCurrentTreeWidget(), 0) # tableWidget.selectRow(currentRow + 1) # This doesn't de-select the last message if you try to mark it unread, but that doesn't interfere. Might not be necessary. # We could also select upwards, but then our problem would be with the topmost message. @@ -3953,54 +3989,38 @@ class MyForm(settingsmixin.SMainWindow): messageTextedit = self.getCurrentMessageTextedit() if not messageTextedit: return - queryreturn = [] - message = "" - if folder == 'sent': - ackdata = self.getCurrentMessageId() - if ackdata and messageTextedit: - queryreturn = sqlQuery( - '''select message, 1 from sent where ackdata=?''', ackdata) + msgid = self.getCurrentMessageId() + if msgid: + queryreturn = sqlQuery( + '''SELECT message FROM %s WHERE %s=?''' % ( + ('sent', 'ackdata') if folder == 'sent' + else ('inbox', 'msgid') + ), msgid + ) + + try: + message = queryreturn[-1][0] + except NameError: + message = "" + except IndexError: + message = _translate( + "MainWindow", + "Error occurred: could not load message from disk." + ) else: - msgid = self.getCurrentMessageId() - if msgid and messageTextedit: - queryreturn = sqlQuery( - '''select message, read from inbox where msgid=?''', msgid) - - if queryreturn != []: - refresh = False - propagate = False tableWidget = self.getCurrentMessagelist() currentRow = tableWidget.currentRow() - for row in queryreturn: - message, read = row - if tableWidget.item(currentRow, 0).unread == True: - refresh = True - if folder != 'sent': - markread = sqlExecute( - '''UPDATE inbox SET read = 1 WHERE msgid = ? AND read=0''', msgid) - if markread > 0: - propagate = True - if refresh: - if not tableWidget: - return - font = QtGui.QFont() - font.setBold(False) -# inventoryHashesToMarkRead = [] -# inventoryHashToMarkRead = str(tableWidget.item( -# currentRow, 3).data(Qt.UserRole).toPyObject()) -# inventoryHashesToMarkRead.append(inventoryHashToMarkRead) - tableWidget.item(currentRow, 0).setUnread(False) - tableWidget.item(currentRow, 1).setUnread(False) - tableWidget.item(currentRow, 2).setUnread(False) - tableWidget.item(currentRow, 3).setFont(font) - if propagate: - self.propagateUnreadCount(tableWidget.item(currentRow, 1 if tableWidget.item(currentRow, 1).type == AccountMixin.SUBSCRIPTION else 0).data(QtCore.Qt.UserRole), folder, self.getCurrentTreeWidget(), -1) + # refresh + if tableWidget.item(currentRow, 0).unread is True: + self.updateUnreadStatus(tableWidget, currentRow, msgid) + # propagate + if folder != 'sent' and sqlExecute( + '''UPDATE inbox SET read=1 WHERE msgid=? AND read=0''', + msgid + ) > 0: + self.propagateUnreadCount() - else: - data = self.getCurrentMessageId() - if data != False: - message = "Error occurred: could not load message from disk." messageTextedit.setCurrentFont(QtGui.QFont()) messageTextedit.setTextColor(QtGui.QColor()) messageTextedit.setContent(message) From 3b3e4de9d7fc22e7ff011c38043e6904844fd051 Mon Sep 17 00:00:00 2001 From: Dmitri Bogomolov <4glitch@gmail.com> Date: Tue, 6 Feb 2018 17:33:19 +0200 Subject: [PATCH 383/407] Zero Inventory().numberOfInventoryLookupsPerformed before update started because "Inventory lookups per second" is calculated by /2 (for 2 sec) --- src/bitmessageqt/networkstatus.py | 1 + 1 file changed, 1 insertion(+) diff --git a/src/bitmessageqt/networkstatus.py b/src/bitmessageqt/networkstatus.py index 02c0deaf..06b1e0ce 100644 --- a/src/bitmessageqt/networkstatus.py +++ b/src/bitmessageqt/networkstatus.py @@ -46,6 +46,7 @@ class NetworkStatus(QtGui.QWidget, RetranslateMixin): self.timer, QtCore.SIGNAL("timeout()"), self.runEveryTwoSeconds) def startUpdate(self): + Inventory().numberOfInventoryLookupsPerformed = 0 self.runEveryTwoSeconds() self.timer.start(2000) # milliseconds From d083c53e1b0c1c6470086fe87514afd1204b6c07 Mon Sep 17 00:00:00 2001 From: Dmitri Bogomolov <4glitch@gmail.com> Date: Mon, 5 Feb 2018 16:40:43 +0200 Subject: [PATCH 384/407] New button "Clear" on tab "Send" to clear all fields (#919) --- src/bitmessageqt/__init__.py | 9 ++++++++- src/bitmessageqt/bitmessageui.py | 4 ++++ 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/src/bitmessageqt/__init__.py b/src/bitmessageqt/__init__.py index 76940ec0..51d26388 100644 --- a/src/bitmessageqt/__init__.py +++ b/src/bitmessageqt/__init__.py @@ -147,6 +147,8 @@ class MyForm(settingsmixin.SMainWindow): "clicked()"), self.click_pushButtonAddSubscription) QtCore.QObject.connect(self.ui.pushButtonTTL, QtCore.SIGNAL( "clicked()"), self.click_pushButtonTTL) + QtCore.QObject.connect(self.ui.pushButtonClear, QtCore.SIGNAL( + "clicked()"), self.click_pushButtonClear) QtCore.QObject.connect(self.ui.pushButtonSend, QtCore.SIGNAL( "clicked()"), self.click_pushButtonSend) QtCore.QObject.connect(self.ui.pushButtonFetchNamecoinID, QtCore.SIGNAL( @@ -1840,7 +1842,6 @@ class MyForm(settingsmixin.SMainWindow): def rerenderSubscriptions(self): self.rerenderTabTreeSubscriptions() - def click_pushButtonTTL(self): QtGui.QMessageBox.information(self, 'Time To Live', _translate( "MainWindow", """The TTL, or Time-To-Live is the length of time that the network will hold the message. @@ -1848,6 +1849,12 @@ class MyForm(settingsmixin.SMainWindow): will resend the message automatically. The longer the Time-To-Live, the more work your computer must do to send the message. A Time-To-Live of four or five days is often appropriate."""), QtGui.QMessageBox.Ok) + def click_pushButtonClear(self): + self.ui.lineEditSubject.setText("") + self.ui.lineEditTo.setText("") + self.ui.textEditMessage.setText("") + self.ui.comboBoxSendFrom.setCurrentIndex(0) + def click_pushButtonSend(self): encoding = 3 if QtGui.QApplication.queryKeyboardModifiers() & QtCore.Qt.ShiftModifier else 2 diff --git a/src/bitmessageqt/bitmessageui.py b/src/bitmessageqt/bitmessageui.py index f5d28a7f..cb3578c0 100644 --- a/src/bitmessageqt/bitmessageui.py +++ b/src/bitmessageqt/bitmessageui.py @@ -337,6 +337,9 @@ class Ui_MainWindow(object): self.labelHumanFriendlyTTLDescription.setMinimumSize(QtCore.QSize(45, 0)) self.labelHumanFriendlyTTLDescription.setObjectName(_fromUtf8("labelHumanFriendlyTTLDescription")) self.horizontalLayout_5.addWidget(self.labelHumanFriendlyTTLDescription, 1, QtCore.Qt.AlignLeft) + self.pushButtonClear = QtGui.QPushButton(self.send) + self.pushButtonClear.setObjectName(_fromUtf8("pushButtonClear")) + self.horizontalLayout_5.addWidget(self.pushButtonClear, 0, QtCore.Qt.AlignRight) self.pushButtonSend = QtGui.QPushButton(self.send) self.pushButtonSend.setObjectName(_fromUtf8("pushButtonSend")) self.horizontalLayout_5.addWidget(self.pushButtonSend, 0, QtCore.Qt.AlignRight) @@ -701,6 +704,7 @@ class Ui_MainWindow(object): except: pass self.labelHumanFriendlyTTLDescription.setText(_translate("MainWindow", "%n hour(s)", None, QtCore.QCoreApplication.CodecForTr, hours)) + self.pushButtonClear.setText(_translate("MainWindow", "Clear", None)) self.pushButtonSend.setText(_translate("MainWindow", "Send", None)) self.tabWidget.setTabText(self.tabWidget.indexOf(self.send), _translate("MainWindow", "Send", None)) self.treeWidgetSubscriptions.headerItem().setText(0, _translate("MainWindow", "Subscriptions", None)) From 3d1fa473fb270339d8b326a31f2357f20fb71d9d Mon Sep 17 00:00:00 2001 From: Peter Surda Date: Tue, 6 Feb 2018 22:28:56 +0100 Subject: [PATCH 385/407] Dandelion updates - expiration now uses poisson distribution just like in the bitcoin version --- src/network/dandelion.py | 25 ++++++++++++++++++------- 1 file changed, 18 insertions(+), 7 deletions(-) diff --git a/src/network/dandelion.py b/src/network/dandelion.py index edd4fb5b..bee01d73 100644 --- a/src/network/dandelion.py +++ b/src/network/dandelion.py @@ -1,5 +1,5 @@ from collections import namedtuple -from random import choice, sample +from random import choice, sample, expovariate from threading import RLock from time import time @@ -12,8 +12,11 @@ import state # randomise routes after 600 seconds REASSIGN_INTERVAL = 600 -# trigger fluff due to expiration in 2 minutes -FLUFF_TRIGGER_TIMEOUT = 120 + +# trigger fluff due to expiration +FLUFF_TRIGGER_FIXED_DELAY = 10 +FLUFF_TRIGGER_MEAN_DELAY = 30 + MAX_STEMS = 2 Stem = namedtuple('Stem', ['child', 'stream', 'timeout']) @@ -31,6 +34,14 @@ class Dandelion(): self.refresh = time() + REASSIGN_INTERVAL self.lock = RLock() + @staticmethod + def poissonTimeout(start=None, average=0): + if start is None: + start = time() + if average == 0: + average = FLUFF_TRIGGER_MEAN_DELAY + return start + expovariate(1.0/average) + FLUFF_TRIGGER_FIXED_DELAY + def addHash(self, hashId, source=None, stream=1): if not state.dandelion: return @@ -38,7 +49,7 @@ class Dandelion(): self.hashMap[hashId] = Stem( self.getNodeStem(source), stream, - time() + FLUFF_TRIGGER_TIMEOUT) + Dandelion.poissonTimeout()) def setHashStream(self, hashId, stream=1): with self.lock: @@ -46,7 +57,7 @@ class Dandelion(): self.hashMap[hashId] = Stem( self.hashMap[hashId].child, stream, - time() + FLUFF_TRIGGER_TIMEOUT) + Dandelion.poissonTimeout()) def removeHash(self, hashId, reason="no reason specified"): logging.debug("%s entering fluff mode due to %s.", ''.join('%02x'%ord(i) for i in hashId), reason) @@ -70,7 +81,7 @@ class Dandelion(): for k in (k for k, v in self.nodeMap.iteritems() if v is None): self.nodeMap[k] = connection for k, v in {k: v for k, v in self.hashMap.iteritems() if v.child is None}.iteritems(): - self.hashMap[k] = Stem(connection, v.stream, time() + FLUFF_TRIGGER_TIMEOUT) + self.hashMap[k] = Stem(connection, v.stream, Dandelion.poissionTimeout()) invQueue.put((v.stream, k, v.child)) @@ -83,7 +94,7 @@ class Dandelion(): for k in (k for k, v in self.nodeMap.iteritems() if v == connection): self.nodeMap[k] = None for k, v in {k: v for k, v in self.hashMap.iteritems() if v.child == connection}.iteritems(): - self.hashMap[k] = Stem(None, v.stream, time() + FLUFF_TRIGGER_TIMEOUT) + self.hashMap[k] = Stem(None, v.stream, Dandelion.poissonTimeout()) def pickStem(self, parent=None): try: From c6834093ee07406b385a81c5994977fab8a42b1e Mon Sep 17 00:00:00 2001 From: Peter Surda Date: Tue, 6 Feb 2018 23:55:41 +0100 Subject: [PATCH 386/407] QT sqlite conversion fix - QByteArray will be stored as str in the db --- src/bitmessageqt/__init__.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/bitmessageqt/__init__.py b/src/bitmessageqt/__init__.py index 51d26388..9fb5fb29 100644 --- a/src/bitmessageqt/__init__.py +++ b/src/bitmessageqt/__init__.py @@ -33,6 +33,7 @@ from pyelliptic.openssl import OpenSSL import textwrap import debug import random +from sqlite3 import register_adapter import string from datetime import datetime, timedelta from helper_ackPayload import genAckPayload @@ -1328,6 +1329,10 @@ class MyForm(settingsmixin.SMainWindow): self._player(soundFilename) + # Adapters and converters for QT <-> sqlite + def sqlInit(self): + register_adapter(QtCore.QByteArray, str) + # Try init the distro specific appindicator, # for example the Ubuntu MessagingMenu def indicatorInit(self): @@ -4396,6 +4401,7 @@ def run(): app.setStyleSheet("QStatusBar::item { border: 0px solid black }") myapp = MyForm() + myapp.sqlInit() myapp.appIndicatorInit(app) myapp.indicatorInit() myapp.notifierInit() From 08bb85a95254a54cf9182d0f452b29bf904f5100 Mon Sep 17 00:00:00 2001 From: Peter Surda Date: Wed, 7 Feb 2018 17:22:26 +0100 Subject: [PATCH 387/407] Dandelion staticmethod fix --- src/network/dandelion.py | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/src/network/dandelion.py b/src/network/dandelion.py index bee01d73..098a9a22 100644 --- a/src/network/dandelion.py +++ b/src/network/dandelion.py @@ -34,8 +34,7 @@ class Dandelion(): self.refresh = time() + REASSIGN_INTERVAL self.lock = RLock() - @staticmethod - def poissonTimeout(start=None, average=0): + def poissonTimeout(self, start=None, average=0): if start is None: start = time() if average == 0: @@ -49,7 +48,7 @@ class Dandelion(): self.hashMap[hashId] = Stem( self.getNodeStem(source), stream, - Dandelion.poissonTimeout()) + self.poissonTimeout()) def setHashStream(self, hashId, stream=1): with self.lock: @@ -57,7 +56,7 @@ class Dandelion(): self.hashMap[hashId] = Stem( self.hashMap[hashId].child, stream, - Dandelion.poissonTimeout()) + self.poissonTimeout()) def removeHash(self, hashId, reason="no reason specified"): logging.debug("%s entering fluff mode due to %s.", ''.join('%02x'%ord(i) for i in hashId), reason) @@ -81,7 +80,7 @@ class Dandelion(): for k in (k for k, v in self.nodeMap.iteritems() if v is None): self.nodeMap[k] = connection for k, v in {k: v for k, v in self.hashMap.iteritems() if v.child is None}.iteritems(): - self.hashMap[k] = Stem(connection, v.stream, Dandelion.poissionTimeout()) + self.hashMap[k] = Stem(connection, v.stream, self.poissionTimeout()) invQueue.put((v.stream, k, v.child)) @@ -94,7 +93,7 @@ class Dandelion(): for k in (k for k, v in self.nodeMap.iteritems() if v == connection): self.nodeMap[k] = None for k, v in {k: v for k, v in self.hashMap.iteritems() if v.child == connection}.iteritems(): - self.hashMap[k] = Stem(None, v.stream, Dandelion.poissonTimeout()) + self.hashMap[k] = Stem(None, v.stream, self.poissonTimeout()) def pickStem(self, parent=None): try: From f870bcc6f7bc03ae4fa4380aa8647c60d44ec190 Mon Sep 17 00:00:00 2001 From: Peter Surda Date: Thu, 8 Feb 2018 06:52:33 +0100 Subject: [PATCH 388/407] More lightweight URI regexp - the old one can take a lot of resources and be misused for a DoS - this still nees to be tested if it is flexible enough - also fix link click popup --- src/bitmessageqt/messageview.py | 2 +- src/bitmessageqt/safehtmlparser.py | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/src/bitmessageqt/messageview.py b/src/bitmessageqt/messageview.py index de357e23..751d4ff7 100644 --- a/src/bitmessageqt/messageview.py +++ b/src/bitmessageqt/messageview.py @@ -70,7 +70,7 @@ class MessageView(QtGui.QTextBrowser): return reply = QtGui.QMessageBox.warning(self, QtGui.QApplication.translate("MessageView", "Follow external link"), - QtGui.QApplication.translate("MessageView", "The link \"%1\" will open in a browser. It may be a security risk, it could de-anonymise you or download malicious data. Are you sure?").arg(str(link.toString())), + QtGui.QApplication.translate("MessageView", "The link \"%1\" will open in a browser. It may be a security risk, it could de-anonymise you or download malicious data. Are you sure?").arg(unicode(link.toString())), QtGui.QMessageBox.Yes, QtGui.QMessageBox.No) if reply == QtGui.QMessageBox.Yes: QtGui.QDesktopServices.openUrl(link) diff --git a/src/bitmessageqt/safehtmlparser.py b/src/bitmessageqt/safehtmlparser.py index 88431855..d1d7910c 100644 --- a/src/bitmessageqt/safehtmlparser.py +++ b/src/bitmessageqt/safehtmlparser.py @@ -22,7 +22,8 @@ class SafeHTMLParser(HTMLParser): replaces_pre = [["&", "&"], ["\"", """], ["<", "<"], [">", ">"]] replaces_post = [["\n", "
"], ["\t", "    "], [" ", "  "], [" ", "  "], ["
", "
 "]] src_schemes = [ "data" ] - uriregex1 = re.compile(r'(?i)\b((?:(https?|ftp|bitcoin):(?:/{1,3}|[a-z0-9%])|www\d{0,3}[.]|[a-z0-9.\-]+[.][a-z]{2,4}/)(?:[^\s()<>]+|\(([^\s()<>]+|(\([^\s()<>]+\)))*\))+(?:\(([^\s()<>]+|(\([^\s()<>]+\)))*\)|[^\s`!()\[\]{};:\'".,<>?]))') + #uriregex1 = re.compile(r'(?i)\b((?:(https?|ftp|bitcoin):(?:/{1,3}|[a-z0-9%])|www\d{0,3}[.]|[a-z0-9.\-]+[.][a-z]{2,4}/)(?:[^\s()<>]+|\(([^\s()<>]+|(\([^\s()<>]+\)))*\))+(?:\(([^\s()<>]+|(\([^\s()<>]+\)))*\)|[^\s`!()\[\]{};:\'".,<>?]))') + uriregex1 = re.compile(r'((https?|ftp|bitcoin):(?:/{1,3}|[a-z0-9%])(?:[a-zA-Z]|[0-9]|[$-_@.&+#]|[!*\(\),]|(?:%[0-9a-fA-F][0-9a-fA-F]))+)') uriregex2 = re.compile(r' Date: Thu, 8 Feb 2018 12:58:38 +0200 Subject: [PATCH 389/407] Fixed trailing '>' in message view which appeared after HTLM rendering --- src/bitmessageqt/messageview.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/bitmessageqt/messageview.py b/src/bitmessageqt/messageview.py index 751d4ff7..4d2e768d 100644 --- a/src/bitmessageqt/messageview.py +++ b/src/bitmessageqt/messageview.py @@ -103,7 +103,7 @@ class MessageView(QtGui.QTextBrowser): if self.mode == MessageView.MODE_HTML: pos = self.out.find(">", self.outpos) if pos > self.outpos: - self.outpos = pos + self.outpos = pos + 1 cursor.movePosition(QtGui.QTextCursor.End, QtGui.QTextCursor.MoveAnchor) cursor.insertHtml(QtCore.QString(self.out[startpos:self.outpos])) self.verticalScrollBar().setValue(position) From 066b419e16d8a04eb7b54590e864f95f99ce6d09 Mon Sep 17 00:00:00 2001 From: Peter Surda Date: Fri, 9 Feb 2018 00:49:08 +0100 Subject: [PATCH 390/407] Bugfixes - typo in dandelion - stealth ackdata fix for broadcasts and mailing lists --- src/bitmessageqt/__init__.py | 1 + src/class_objectProcessor.py | 8 +++++--- src/network/dandelion.py | 2 +- 3 files changed, 7 insertions(+), 4 deletions(-) diff --git a/src/bitmessageqt/__init__.py b/src/bitmessageqt/__init__.py index 9fb5fb29..3230a803 100644 --- a/src/bitmessageqt/__init__.py +++ b/src/bitmessageqt/__init__.py @@ -2095,6 +2095,7 @@ class MyForm(settingsmixin.SMainWindow): # We don't actually need the ackdata for acknowledgement since # this is a broadcast message, but we can use it to update the # user interface when the POW is done generating. + streamNumber = decodeAddress(fromAddress)[2] ackdata = genAckPayload(streamNumber, 0) toAddress = str_broadcast_subscribers ripe = '' diff --git a/src/class_objectProcessor.py b/src/class_objectProcessor.py index addcb24b..6387f6a7 100644 --- a/src/class_objectProcessor.py +++ b/src/class_objectProcessor.py @@ -553,7 +553,9 @@ class objectProcessor(threading.Thread): message = time.strftime("%a, %Y-%m-%d %H:%M:%S UTC", time.gmtime( )) + ' Message ostensibly from ' + fromAddress + ':\n\n' + body fromAddress = toAddress # The fromAddress for the broadcast that we are about to send is the toAddress (my address) for the msg message we are currently processing. - # We don't actually need the ackdataForBroadcast for acknowledgement since this is a broadcast message but we can use it to update the user interface when the POW is done generating. + # We don't actually need the ackdata for acknowledgement since this is a broadcast message but we can use it to update the user interface when the POW is done generating. + streamNumber = decodeAddress(fromAddress)[2] + ackdata = genAckPayload(streamNumber, 0) toAddress = '[Broadcast subscribers]' ripe = '' @@ -568,7 +570,7 @@ class objectProcessor(threading.Thread): fromAddress, subject, message, - ackdataForBroadcast, + ackdata, int(time.time()), # sentTime (this doesn't change) int(time.time()), # lastActionTime 0, @@ -580,7 +582,7 @@ class objectProcessor(threading.Thread): helper_sent.insert(t) queues.UISignalQueue.put(('displayNewSentMessage', ( - toAddress, '[Broadcast subscribers]', fromAddress, subject, message, ackdataForBroadcast))) + toAddress, '[Broadcast subscribers]', fromAddress, subject, message, ackdata))) queues.workerQueue.put(('sendbroadcast', '')) # Don't send ACK if invalid, blacklisted senders, invisible messages, disabled or chan diff --git a/src/network/dandelion.py b/src/network/dandelion.py index 098a9a22..06ecca24 100644 --- a/src/network/dandelion.py +++ b/src/network/dandelion.py @@ -80,7 +80,7 @@ class Dandelion(): for k in (k for k, v in self.nodeMap.iteritems() if v is None): self.nodeMap[k] = connection for k, v in {k: v for k, v in self.hashMap.iteritems() if v.child is None}.iteritems(): - self.hashMap[k] = Stem(connection, v.stream, self.poissionTimeout()) + self.hashMap[k] = Stem(connection, v.stream, self.poissonTimeout()) invQueue.put((v.stream, k, v.child)) From cf8ed3624021db78bc15ed5f96f4314f795ba0eb Mon Sep 17 00:00:00 2001 From: Peter Surda Date: Fri, 9 Feb 2018 11:11:48 +0100 Subject: [PATCH 391/407] Windows Qt refactoring fix --- src/bitmessageqt/__init__.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/bitmessageqt/__init__.py b/src/bitmessageqt/__init__.py index 3230a803..7bbd7dd9 100644 --- a/src/bitmessageqt/__init__.py +++ b/src/bitmessageqt/__init__.py @@ -598,7 +598,7 @@ class MyForm(settingsmixin.SMainWindow): if 'win32' in sys.platform or 'win64' in sys.platform: # Auto-startup for Windows RUN_PATH = "HKEY_CURRENT_USER\\Software\\Microsoft\\Windows\\CurrentVersion\\Run" - self.settings = QSettings(RUN_PATH, QSettings.NativeFormat) + self.settings = QtCore.QSettings(RUN_PATH, QtCore.QSettings.NativeFormat) self.settings.remove( "PyBitmessage") # In case the user moves the program and the registry entry is no longer valid, this will delete the old registry entry. if BMConfigParser().getboolean('bitmessagesettings', 'startonlogon'): @@ -2546,7 +2546,7 @@ class MyForm(settingsmixin.SMainWindow): if 'win32' in sys.platform or 'win64' in sys.platform: # Auto-startup for Windows RUN_PATH = "HKEY_CURRENT_USER\\Software\\Microsoft\\Windows\\CurrentVersion\\Run" - self.settings = QSettings(RUN_PATH, QSettings.NativeFormat) + self.settings = QtCore.QSettings(RUN_PATH, QtCore.QSettings.NativeFormat) if BMConfigParser().getboolean('bitmessagesettings', 'startonlogon'): self.settings.setValue("PyBitmessage", sys.argv[0]) else: From a69daa13b9d6e8c89fc41e856bc8385fc6cff17e Mon Sep 17 00:00:00 2001 From: Peter Surda Date: Mon, 12 Feb 2018 13:48:59 +0100 Subject: [PATCH 392/407] Pluralisation --- src/bitmessageqt/address_dialogs.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/bitmessageqt/address_dialogs.py b/src/bitmessageqt/address_dialogs.py index 7b619625..6bb5028b 100644 --- a/src/bitmessageqt/address_dialogs.py +++ b/src/bitmessageqt/address_dialogs.py @@ -194,7 +194,7 @@ class NewSubscriptionDialog(AddressDataDialog, RetranslateMixin): self.checkBoxDisplayMessagesAlreadyInInventory.setText( _translate( "MainWindow", - "Display the %1 recent broadcast(s) from this address." + "Display the %n recent broadcast(s) from this address." ).arg(count)) From eefc967737c90c3f715153aad113f2bb71fa1fca Mon Sep 17 00:00:00 2001 From: Peter Surda Date: Mon, 12 Feb 2018 14:10:51 +0100 Subject: [PATCH 393/407] Pluralisation --- src/bitmessageqt/address_dialogs.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/bitmessageqt/address_dialogs.py b/src/bitmessageqt/address_dialogs.py index 6bb5028b..71219a01 100644 --- a/src/bitmessageqt/address_dialogs.py +++ b/src/bitmessageqt/address_dialogs.py @@ -194,8 +194,11 @@ class NewSubscriptionDialog(AddressDataDialog, RetranslateMixin): self.checkBoxDisplayMessagesAlreadyInInventory.setText( _translate( "MainWindow", - "Display the %n recent broadcast(s) from this address." - ).arg(count)) + "Display the %n recent broadcast(s) from this address.", + None, + QtCore.QCoreApplication.CodecForTr, + count + ) class RegenerateAddressesDialog(QtGui.QDialog, RetranslateMixin): From 2bc2b6bc5be3eabce07bc83284582b642a9b4061 Mon Sep 17 00:00:00 2001 From: Peter Surda Date: Mon, 12 Feb 2018 14:49:43 +0100 Subject: [PATCH 394/407] Typo --- src/bitmessageqt/address_dialogs.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/bitmessageqt/address_dialogs.py b/src/bitmessageqt/address_dialogs.py index 71219a01..2ea5cef8 100644 --- a/src/bitmessageqt/address_dialogs.py +++ b/src/bitmessageqt/address_dialogs.py @@ -198,7 +198,7 @@ class NewSubscriptionDialog(AddressDataDialog, RetranslateMixin): None, QtCore.QCoreApplication.CodecForTr, count - ) + )) class RegenerateAddressesDialog(QtGui.QDialog, RetranslateMixin): From eb97face6170984a579cb5c597998357eacb9c50 Mon Sep 17 00:00:00 2001 From: Peter Surda Date: Mon, 12 Feb 2018 17:07:54 +0100 Subject: [PATCH 395/407] Show traceback in protocol parser error handler --- src/network/advanceddispatcher.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/network/advanceddispatcher.py b/src/network/advanceddispatcher.py index 31555c62..6f857398 100644 --- a/src/network/advanceddispatcher.py +++ b/src/network/advanceddispatcher.py @@ -61,7 +61,7 @@ class AdvancedDispatcher(asyncore.dispatcher): if not getattr(self, "state_" + str(self.state))(): break except AttributeError: - logger.error("Unknown state %s", self.state) + logger.error("Unknown state %s", self.state, exc_info=True) raise except BusyError: return False From 6bcbad63b9f2a385b421eeb8a4db2404b0ab198f Mon Sep 17 00:00:00 2001 From: Dmitri Bogomolov <4glitch@gmail.com> Date: Mon, 12 Feb 2018 13:21:31 +0200 Subject: [PATCH 396/407] Marked folder names for translation --- src/bitmessageqt/foldertree.py | 39 ++++++++++++++++++++++------------ 1 file changed, 25 insertions(+), 14 deletions(-) diff --git a/src/bitmessageqt/foldertree.py b/src/bitmessageqt/foldertree.py index 7cc42229..11227fca 100644 --- a/src/bitmessageqt/foldertree.py +++ b/src/bitmessageqt/foldertree.py @@ -1,12 +1,20 @@ from PyQt4 import QtCore, QtGui from string import find, rfind, rstrip, lstrip +from tr import _translate from bmconfigparser import BMConfigParser from helper_sql import * from utils import * from settingsmixin import SettingsMixin -class AccountMixin (object): +# for pylupdate +_translate("MainWindow", "inbox") +_translate("MainWindow", "new") +_translate("MainWindow", "sent") +_translate("MainWindow", "trash") + + +class AccountMixin(object): ALL = 0 NORMAL = 1 CHAN = 2 @@ -97,7 +105,8 @@ class AccountMixin (object): retval, = row retval = unicode(retval, 'utf-8') elif self.address is None or self.type == AccountMixin.ALL: - return unicode(str(QtGui.QApplication.translate("MainWindow", "All accounts")), 'utf-8') + return unicode( + str(_translate("MainWindow", "All accounts")), 'utf-8') if retval is None: return unicode(self.address, 'utf-8') else: @@ -115,17 +124,16 @@ class Ui_FolderWidget(QtGui.QTreeWidgetItem, AccountMixin): def setFolderName(self, fname): self.folderName = str(fname) - + def data(self, column, role): if column == 0: if role == QtCore.Qt.DisplayRole: - return QtGui.QApplication.translate("MainWindow", self.folderName) + (" (" + str(self.unreadCount) + ")" if self.unreadCount > 0 else "") - elif role == QtCore.Qt.EditRole: - return QtGui.QApplication.translate("MainWindow", self.folderName) - elif role == QtCore.Qt.ToolTipRole: - return QtGui.QApplication.translate("MainWindow", self.folderName) - elif role == QtCore.Qt.DecorationRole: - pass + return _translate("MainWindow", self.folderName) + ( + " (" + str(self.unreadCount) + ")" + if self.unreadCount > 0 else "" + ) + elif role in (QtCore.Qt.EditRole, QtCore.Qt.ToolTipRole): + return _translate("MainWindow", self.folderName) elif role == QtCore.Qt.FontRole: font = QtGui.QFont() font.setBold(self.unreadCount > 0) @@ -166,16 +174,19 @@ class Ui_AddressWidget(QtGui.QTreeWidgetItem, AccountMixin, SettingsMixin): self.setEnabled(enabled) self.setUnreadCount(unreadCount) self.setType() - + def _getLabel(self): if self.address is None: - return unicode(QtGui.QApplication.translate("MainWindow", "All accounts").toUtf8(), 'utf-8', 'ignore') + return unicode(_translate( + "MainWindow", "All accounts").toUtf8(), 'utf-8', 'ignore') else: try: - return unicode(BMConfigParser().get(self.address, 'label'), 'utf-8', 'ignore') + return unicode( + BMConfigParser().get(self.address, 'label'), + 'utf-8', 'ignore') except: return unicode(self.address, 'utf-8') - + def _getAddressBracket(self, unreadCount = False): ret = "" if unreadCount: From 022ee0917709852799f774e50faa87bc796efa96 Mon Sep 17 00:00:00 2001 From: PyBitmessage Translations Date: Mon, 12 Feb 2018 21:33:30 +0100 Subject: [PATCH 397/407] Auto-updated language sk from transifex --- src/translations/bitmessage_sk.qm | Bin 88287 -> 90043 bytes src/translations/bitmessage_sk.ts | 431 ++++++++++++++++-------------- 2 files changed, 233 insertions(+), 198 deletions(-) diff --git a/src/translations/bitmessage_sk.qm b/src/translations/bitmessage_sk.qm index 228eff26de773d42b59882b0de916e0c870184f6..26c2a24d6b127a7fe5aa2c47d03ac662cf8029e2 100644 GIT binary patch delta 4825 zcma)9d3cQ1+rHnKd1qg0ZBg-R8bl&OElE^@P@+uHT5FqR-ZUeb877;c8A|OWI@X9N z(%QA5wAQGl3l-GT+KO7Mma6b;t>67-{C>T@@A|Im`~Db@bKd7X=Q;O%KhHTiUnXuZ z7Yl3p(*U%9P`3u4nFBc50^(=D%0GZ^kwDYdz&qapSv`P(H6R#%1>S86tZE3Pb%xN} z$#49=-wVx=Fd#V@no~o7lpWCAj0WV2psg%~n7#*sFazRD4e((R#Dxz5|H9@Fi#r3; zZ_~a5%&LUAt`tJzeu!U9gV3!U+SO`qG!wdhU1%S{&}Az(&4G1!1<-jcs&%;soH>MQ zWgkH>Zb2Qy16K=BH*YpjdJG{e3V^a%2tD=`7-~f$J-@H4kBGVz@MFmzh*-f>-d%~v z1N9-qtwM`$7j^Y;OvkwN>r4@uYG z24cME9`OO~@8~(-2sp#=)`&3>UR!}aqjwPP*+F}8Hu_B;;fK)v3-ou%2y^C!lB>L+ET z7`v-Ck+fl)rwBruGEC{a9Oz=h!qFkX{WDnRst!y(jnc3Dc84vaok);C!!^f3C``tqIrB(>) zK|+JN8o>9e(6ISd2+dVO6LTnVIzebT{04BZr_eH!>sG85TKV&tVaNGGT(u3rv?oHx z!4zQ0&w~9V1HAr$Fnl00zhV}WVFhwW3u%vUP((EacavR!?vCJzB?7Y>3)6-~0p-_) zd1a*Jl?Y++dLr6Q7PgnFff>cZ&ev80s;5EQZNG5POv(#J3jV)7;l@sf@Jy8mygEoF z?qC874yw%VYruh2RYbxsfOny)*=|Cf+eX#w0&PyEDms@Oe|}We;xF3Ahg9t@OaMmq zS9KjvJF1#0t;2m%x>l9ZY%@2yq;gO70BvNI|9vmeY^Q4a$!ZW@?CV#}R2&M2sAi44 z4Q!53EggOt2s@;P0QGyR7aiV2aek>@^mr%`*s3lW`XLkEuHI(e$Am-G2NrPbO;uOuWFT^R(E9J} zRv(^f2i`KOFBK(G6t&ei`t#&-uBva3&Ih)(Q~&i9)trA$W2-+Kf@s&+hOsJQdueK~ z83^36Xxe|tM%4DICecogPZevr9ikW=gEh%D=JJH2H1cOr>>E2Z=_Vri`yx%&ND+84 zRg?Q9k@bIE(oDF20$9IFGqnx5&6}n9sF-Eca+zjrfxs%*uUQw)jMuc)tc(1F;|H2e z#WjgoZOyhd@ep1buKD_AIE0wCntgd;K$1;!victS@6enszejBy(fpDd0xX*#3TL8# z*!p6#d9(}KiEaN_$c-`=im?aSZXbOu#-&lM^G#x>N;?p@R($iqaaKVOacI6GB1v@i zC)bl9rndf?RF#X~5<;HPSRCK70z!B_apHIxLWc(8)X%#@&>s_L#c`wg<>JEFaNyt+ zvFKhVga%{8#Zwou_V1hhj+A7b4cyLAGF2(?pGl6>Z~n2dzdZPu3Z%%r_$Tn^$~VnJQKB> zX4e3Id0BgWO?8%2AMLq*SB{;l&*?okIqCzWn?>tBGL`MSc|D=3Z_ zUEvibkhD)X?^p?2YlN;SeFJa4p1P&Wn0eww-SWIi?2LK3wKG{(=ohq&(t`HF<)FRU zP50%ST)(hPx2JUpgg0jD&S@EU`F7p;6GV7jh+lWjPLA96(ETfH7qEMXK14ppfCuz3 z+Pe@EuIXd$-UeoR^>4@b<=CVjxN9!Z=$JmYOB7q|YW?&TgMj0H{hX#$|A8&~z-}wB zN2lL0pd%0RRKH_;Jg?h^`U9)>v5&;)5Bfvc=+@~^Pxi3N{O$Co7ja_1Q2pg|LxI6% z`dbf6*iahkAAB$hICs!c^Xl8cvwenoE6HiXI76d@cUi~R4J{Yu0s0e$wu4gucZi|; z4_yB^!_WiIfG?UD&8v}pR%tsHEuOk@<4Xu?g}2rkZjyLo@G0K zit)(g1gh7y)Od056=2^2N~?Vy{mzMQihcy}72?USYf?51Zl#aK7(EQ$me8!0iqud-v(Q zhIdt2p?-zQzRk}Jm$xwWezuw?tYb@?k!*jJhlHAnmV4Fx`$Ywi$9NYA!2cizlKjnNzPEOhySimo{Y&Ihn`Et-5ypqgPu9>*b~c!AtW9e%kUPy9X*aPEaPvNSU-L@hE$JT zVqGQ?8eN?AhvHbEa;LSTIn@dTZI}?W7p4X6?|)lQ+4r zth{v0w&r1ViP%J2OHVdmze8=UzYF0b_nfU=J-!EOAF{o0b3TqI_jxu?0TYr|+eSqa0aYK{gf|$-{DQ6EbQv)2oUJtE6cIRR zTXX0sgwPjl8@gJ7>rHH5H7H@HJ8j#ZbsN|aZQE0ewlv!IqsPuW}k5xZ%JUI0RX*0HWnjOw5oSf!=LuZR+?& z2Oa+$7z8hp;j40V2{I&%FdKqFtm7LaV^D9kpy8W^BPNWHBiZZUrJT>a=Ll z#pm<-ByUPew$CR!FSXVPvgM>i&P` zMZS#A%-e@-29+7`V(^TDL)qXTU|gPL@94m&*qW^x!OjIaRgTX8$5ORN&fRqJh^23w z@DLr5&iI*(tzenMpBpajnoUF%T(dX|Vw^4H^N=|_WCmP;*;k_ch~y__s4P`n6$`bBfaG@1U1NJ!DWBX&iYNF-W+~ zh#o}zRuxL3n~U40Gw1NCl-#IfuJZFURizO)I;U3sF#APM?cWQXQ_0Y@Zw+Srs^izCQUq*(3WLSzN3{Nt1nY zl=OV(>`d9IOiMn8$4L!(vfatDPkO#0kaxq>mI?YKhtuh!R^?2^*~5QanNq6P=azC^ z85vTtEO}F@YKh^L47ns~!LTqP^vgS8!cny!KcjmUXNsV*2!c{fN`+epuY-`Lb1u88 zj1?^?N=hb}QWfC;n_oD;DR3*lAov-$#b`klDHW*5S&@U{;SUNl_%R)_VB^Psw61WY zu%*a0DA`Poz0bv{$j`@wGRdK`m5<+^Fnmx4|8O~KAc=HmldF_8IVD|c!n#-LoTL6J z>ES`hSgX;OWX`B@<c|c9+8-Bif&bjxVd(OR=GAKXUw>A}NQy~W7T1)w%q)en@Vv?ncVl1gBBq>Ws zB}>zkE&DJiOC!qG3_?FL$R7P(`s?kU@0{;@zUO`3=Y2bNQh0DkNN#0x24D}?<}<*r z2Lkp2!U14UATZ1tuyh0_TmXJP4)~jbsV)KkwWM`=H*_$VcM14w-GIOw;H#zpLDt}( z+X2BI&GGdD2w`V{rYHyrJP@yekoF1Klm#JUFkoCgmHwCjBm_gq&jxchgHW;>Y>+9G z`#3tF1}fhnU?WCD9CCz!)Iztj9Po-n%OP)osv@*Jv;j<=2{U*AkD_57lL+kcg2k>R z;J_DjsrU*^jzxDht?yn3D|7ljBM?@*9AU)EBv@Z+57ws<_J7dALVq}{&jj|z!l^P2 zxK|FhHub=iJs6T54}|++=%#Z(7whKuY!Ze)9S=B1V$>!LF|ZM%XDtM?evferh&3cO z#|OW_cg-v?Cp9L{q;;ox@GJ5#0)1CvQj{y*I2MzYYbY8Jpeg`{w8t+N*?@8zqO%Cl zpn-_lX$y=E!lFI&onSSO9O%V#iDqV1+lbODJ{L zQOI9MOuq=ges@}bc?U&*8R^Cz7PziY0P1p4^>YQVd(u)QSWjQpQ|ws5|%pr=t+iAx7#u7l+ zI_BE409X~y22OUO{f04vv643EKZXVR6Y_r=SP*o;{HH9mkv90+$ijP;0;-oR!i5+} z*vwW>u?0%~*v3PYg3eZK>wcng;1PB#n*-L!>_ne^fZ}U&9D0OZmPx?tYS@R}ba2ph z)}-(Px=vCE#|gkDH-%9Se*#=ID6Bm10}*P4%_*XM{vL%*&9@k=u!~+!A{wZ$|3L9m zk;1KJ8NGKwF?1=#$YqMq0dFanO^VqzMU?9CittqtK)(=$aY-a#b3(DEvL)CL?uvx( z>t%{{bLxS@t%|I`M?kkSqhilw`XJC%Q5HxDSKL)phIa*`!W6fboCWJwq^Q_N z)bdflgJO1IdugAw&IS;=Lm2#cWE>I4zJ|ORUv@&&gS?c zhr7DU0E}A5Jxm`C>{!XwO{9w(lYP1R1+hSJ9`~Vy)VyRAuWz3S#`WX%0aOhR*?j9< ze-h_g-o2O%$KI9qGEk0h#q-0;NQ?nj_#o3%x}iTGa=?~+q9q?D5tGlB@$=>gz~?!9 z^cTwc4K=^)?M)!REx)QCr8fEtzahg&_3yBQ&r4!d1u1-fFG8HNm(RD}O~23ag&D1Y zeJlB+xgKDxhw-PM_aXpm`ExPd$Q?Pp(&P-STl3W?-vHAG@b{xFfUH*nyJJg!@IS$3 zBgKt*g44fgWL#>&<km}m>NqWT9+&YPo!MO z&k<%go~B%#6e2T;a*wydQipP|&XGdg(hxA0C}GvXp`?vt!aCP)2lW=xTzUb2MhfX~ zqQK0D2wPXt!K-bAg4M)?bC&R%4;>sFCLH~V-d|rV{CU+#$ z6)e2th@x-^?+3ReryHkiIg2*ze^NQJZW4(mP&s}#slLqxW%!H;>ILVOkvAPE*S(eV zlc^kCa+Irnp_KadS7vxW1{CA zo4kaqdYmf6ekM8L5>-f$4_)k?DlW^D_I;sRInoTw%~zG^cpn&1uS#yci?q>EWlVld zNQRZDHdbT;W45Z&!wRU=ols@%pc@RjsoEJ6PvV=V%1d}iUhdl*yN5Q%`&XJ{ot>(9 z1bx4`lj@9PCYWc9>YkGJ-C?DAaFZC$O;kNGP>S8nRqwl%l7W?}Ekf?mewU1DXXPuR z%uVh5s-7I|fqJ}$514a++P^dv=w6|Y9%2j3HB+zIHHlRGlX|@+ssCc8I^~oOI2Elv z?l+KbQma0G%!3$NufCLXj(j9Xec5P1PM5E)UKs%-<*BQ;&~M*z^`m=JsfBk^zxb3% zeqt2W@0QO6?i7lxYR3a#^2K(0DYwqo#O{}0k<Wecv}-1Ob5CT)6A+NgjI{bM`pG_6Fa#MtXG;Q{Rtg- zPOmBO_(US@uPL1Pl>nt{jz|pzuvBxZoNlD@)|_2R<(m3lb8RJwFI2Djd-7v)OqJ%v zW&&o}Q(~hEsQLUOi7_FhYR@7`!(AsUOpr8>=7F`cl(fre;xV<8+NM+Pdi0dKHq|ZQ4f{ z440-a8b|=&N^2L!QB0MRPa3NU`D-b?!VAposFWf0C2HKHJ<rJrB!vzEjCyczJ*|FFIGYCn0-@NHmy^W}MW z+<;CkaDfq_Q(+AIUL(Q5g+z8;v@ zBv-xtHXFLhcNSI9Xm(I;_%(zK=MSyAwuo{XsBJf!#C1JW+jaU`8p)<>yKkWbW|wGf zQgUga`AKWH<``)vPV4fn6zJ7L+uy_k7`|US*nc=xg<#bBxP7IvxTc+I8v*!q)&}Hk zAqqce!`=~~6N9u7ztQuPeYBCriF89Z?Lx04(oC3k@nYKO%fH%eOuwG(XtuTFoX6B^ih6y3oLh)BSXo zKhOqYw{_Nrtz%{;>a0t8N$)+3)NT1~AC=oyU3xl| z?fDhD?f<3!B^GLRJ3100Dp%c=3>N~jTUXwj#0fOVZmc=ppW7Ub&;Hg`8ECYsp8q{E z^J}`7W4qEo@3PC4eMAxg>bOro*P0lBtA5#!7Vu-@2YpiY zAz;xceYQmv<<3o?TlST19;PoCssn0U=u0|e0@J+p$L7`30I^+v<_C&9x9e|27^ru+ z>L0sPeXlX@mailchuck.com
- + Registration failed: Registrácia zlyhala: - + The requested email address is not available, please try a new one. Požadovaná e-mailová adresa nie je k dispozícii, skúste znova. - + Sending email gateway registration request Odosielam žiadosť o registráciu na e-mailovej bráne - + Sending email gateway unregistration request Odosielam žiadosť o odhlásenie z e-mailovej brány - + Sending email gateway status request Odosielam žiadosť o stav e-mailovej brány @@ -200,52 +200,52 @@ Please type the desired email address (including @mailchuck.com) below: MainWindow - + Reply to sender Odpovedať odosielateľovi - + Reply to channel Odpoveď na kanál - + Add sender to your Address Book Pridať odosielateľa do adresára - + Add sender to your Blacklist Pridať odosielateľa do svojho zoznamu zakázaných - + Move to Trash Presunúť do koša - + Undelete Obnoviť - + View HTML code as formatted text Zobraziť HTML kód ako formátovaný text - + Save message as... Uložiť správu ako... - + Mark Unread Označiť ako neprečítané - + New Nová @@ -270,12 +270,12 @@ Please type the desired email address (including @mailchuck.com) below: Kopírovať adresu do clipboardu - + Special address behavior... Zvláštne správanie adresy... - + Email gateway E-mailová brána @@ -285,37 +285,37 @@ Please type the desired email address (including @mailchuck.com) below: Zmazať - + Send message to this address Poslať správu na túto adresu - + Subscribe to this address Prihlásiť sa k odberu tejto adresy - + Add New Address Pridať novú adresu - + Copy destination address to clipboard Kopírovať cieľovú adresu do clipboardu - + Force send Vynútiť odoslanie - + One of your addresses, %1, is an old version 1 address. Version 1 addresses are no longer supported. May we delete it now? Jedna z vašich adries, %1, je stará verzia adresy, 1. Verzie adresy 1 už nie sú podporované. Odstrániť ju teraz? - + Waiting for their encryption key. Will request it again soon. Čakanie na šifrovací kľúč príjemcu. Čoskoro bude vyžiadaný znova. @@ -325,17 +325,17 @@ Please type the desired email address (including @mailchuck.com) below: - + Queued. Vo fronte. - + Message sent. Waiting for acknowledgement. Sent at %1 Správa odoslaná. Čakanie na potvrdenie. Odoslaná %1 - + Message sent. Sent at %1 Správa odoslaná. Odoslaná %1 @@ -345,77 +345,77 @@ Please type the desired email address (including @mailchuck.com) below: - + Acknowledgement of the message received %1 Potvrdenie prijatia správy %1 - + Broadcast queued. Rozoslanie vo fronte. - + Broadcast on %1 Rozoslané 1% - + Problem: The work demanded by the recipient is more difficult than you are willing to do. %1 Problém: práca požadovná príjemcom je oveľa ťažšia, než je povolené v nastaveniach. %1 - + Problem: The recipient's encryption key is no good. Could not encrypt message. %1 Problém: šifrovací kľúč príjemcu je nesprávny. Nie je možné zašifrovať správu. %1 - + Forced difficulty override. Send should start soon. Obmedzenie obtiažnosti práce zrušené. Odosielanie by malo čoskoro začať. - + Unknown status: %1 %2 Neznámy stav: %1 %2 - + Not Connected Nepripojený - + Show Bitmessage Ukázať Bitmessage - + Send Odoslať - + Subscribe Prihlásiť sa k odberu - + Channel Kanál - + Quit Ukončiť - + You may manage your keys by editing the keys.dat file stored in the same directory as this program. It is important that you back up this file. Kľúče môžete spravovať úpravou súboru keys.dat, ktorý je uložený v rovnakom adresári ako tento program. Tento súbor je dôležité zálohovať. - + You may manage your keys by editing the keys.dat file stored in %1 It is important that you back up this file. @@ -424,17 +424,17 @@ It is important that you back up this file. Tento súbor je dôležité zálohovať. - + Open keys.dat? Otvoriť keys.dat? - + You may manage your keys by editing the keys.dat file stored in the same directory as this program. It is important that you back up this file. Would you like to open the file now? (Be sure to close Bitmessage before making any changes.) Kľúče môžete spravovať úpravou súboru keys.dat, ktorý je uložený v rovnakom adresári ako tento program. Tento súbor je dôležité zálohovať. Chcete tento súbor teraz otvoriť? (Nezabudnite zatvoriť Bitmessage pred vykonaním akýchkoľvek zmien.) - + You may manage your keys by editing the keys.dat file stored in %1 It is important that you back up this file. Would you like to open the file now? (Be sure to close Bitmessage before making any changes.) @@ -443,37 +443,37 @@ It is important that you back up this file. Would you like to open the file now? Tento súbor je dôležité zálohovať. Chcete tento súbor teraz otvoriť? (Nezabudnite zatvoriť Bitmessage pred vykonaním akýchkoľvek zmien.) - + Delete trash? Vyprázdniť kôš? - + Are you sure you want to delete all trashed messages? Ste si istí, že chcete všetky správy z koša odstrániť? - + bad passphrase zlé heslo - + You must type your passphrase. If you don't have one then this is not the form for you. Je nutné zadať prístupové heslo. Ak heslo nemáte, tento formulár nie je pre Vás. - + Bad address version number Nesprávne číslo verzie adresy - + Your address version number must be a number: either 3 or 4. Číslo verzie adresy musí byť číslo: buď 3 alebo 4. - + Your address version number must be either 3 or 4. Vaše číslo verzie adresy musí byť buď 3 alebo 4. @@ -543,22 +543,22 @@ Tento súbor je dôležité zálohovať. Chcete tento súbor teraz otvoriť? (Ne - + Connection lost Spojenie bolo stratené - + Connected Spojený - + Message trashed Správa odstránenia - + The TTL, or Time-To-Live is the length of time that the network will hold the message. The recipient must get it during this time. If your Bitmessage client does not hear an acknowledgement, it will resend the message automatically. The longer the Time-To-Live, the @@ -566,17 +566,17 @@ Tento súbor je dôležité zálohovať. Chcete tento súbor teraz otvoriť? (Ne TTL (doba životnosti) je čas, počas ktorého bude sieť udržiavať správu. Príjemca musí správu prijať počas tejto životnosti. Keď odosielateľov Bitmessage nedostane po vypršaní životnosti potvrdenie o prijatí, automaticky správu odošle znova. Čím vyššia doba životnosti, tým viac práce musí počítač odosielateľa vykonat na odoslanie správy. Zvyčajne je vhodná doba životnosti okolo štyroch-piatich dní. - + Message too long Správa je príliš dlhá - + The message that you are trying to send is too long by %1 bytes. (The maximum is 261644 bytes). Please cut it down before sending. Správa, ktorú skúšate poslať, má %1 bajtov naviac. (Maximum je 261 644 bajtov). Prosím pred odoslaním skrátiť. - + Error: Your account wasn't registered at an email gateway. Sending registration now as %1, please wait for the registration to be processed before retrying sending. Chyba: Váš účet nebol registrovaný na e-mailovej bráne. Skúšam registrovať ako %1, prosím počkajte na spracovanie registrácie pred opakovaným odoslaním správy. @@ -621,57 +621,57 @@ Tento súbor je dôležité zálohovať. Chcete tento súbor teraz otvoriť? (Ne - + Error: You must specify a From address. If you don't have one, go to the 'Your Identities' tab. Chyba: musíte zadať adresu "Od". Ak žiadnu nemáte, prejdite na kartu "Vaše identity". - + Address version number Číslo verzie adresy - + Concerning the address %1, Bitmessage cannot understand address version numbers of %2. Perhaps upgrade Bitmessage to the latest version. Čo sa týka adresy %1, Bitmessage nepozná číslo verzie adresy %2. Možno by ste mali upgradenúť Bitmessage na najnovšiu verziu. - + Stream number Číslo prúdu - + Concerning the address %1, Bitmessage cannot handle stream numbers of %2. Perhaps upgrade Bitmessage to the latest version. Čo sa týka adresy %1, Bitmessage nespracováva číslo prúdu %2. Možno by ste mali upgradenúť Bitmessage na najnovšiu verziu. - + Warning: You are currently not connected. Bitmessage will do the work necessary to send the message but it won't send until you connect. Upozornenie: momentálne nie ste pripojení. Bitmessage vykoná prácu potrebnú na odoslanie správy, ale odoslať ju môže, až keď budete pripojení. - + Message queued. Správa vo fronte. - + Your 'To' field is empty. Pole "Komu" je prázdne. - + Right click one or more entries in your address book and select 'Send message to this address'. Vybertie jednu alebo viacero položiek v adresári, pravým tlačidlom myši zvoľte "Odoslať správu na túto adresu". - + Fetched address from namecoin identity. Prebratá adresa z namecoin-ovej identity. - + New Message Nová správa @@ -696,47 +696,47 @@ Tento súbor je dôležité zálohovať. Chcete tento súbor teraz otvoriť? (Ne Zadaná adresa bola neplatná a bude ignorovaná. - + Error: You cannot add the same address to your address book twice. Try renaming the existing one if you want. Chyba: tú istú adresu nemožno pridať do adresára dvakrát. Ak chcete, môžete skúsiť premenovať existujúcu menovku. - + Error: You cannot add the same address to your subscriptions twice. Perhaps rename the existing one if you want. Chyba: nemožno pridať rovnakú adresu k odberu dvakrát. Keď chcete, môžete premenovať existujúci záznam. - + Restart Reštart - + You must restart Bitmessage for the port number change to take effect. Aby sa zmena čísla portu prejavila, musíte reštartovať Bitmessage. - + Bitmessage will use your proxy from now on but you may want to manually restart Bitmessage now to close existing connections (if any). Bitmessage bude odteraz používať proxy, ale ak chcete ukončiť existujúce spojenia, musíte Bitmessage manuálne reštartovať. - + Number needed Číslo potrebné - + Your maximum download and upload rate must be numbers. Ignoring what you typed. Maxímálna rýchlosť príjmu a odoslania musí byť uvedená v číslach. Ignorujem zadané údaje. - + Will not resend ever Nikdy opätovne neodosielať - + Note that the time limit you entered is less than the amount of time Bitmessage waits for the first resend attempt therefore your messages will never be resent. Upozornenie: časový limit, ktorý ste zadali, je menší ako čas, ktorý Bitmessage čaká na prvý pokus o opätovné zaslanie, a preto vaše správy nebudú nikdy opätovne odoslané. @@ -771,22 +771,22 @@ Tento súbor je dôležité zálohovať. Chcete tento súbor teraz otvoriť? (Ne Heslo je skutočne potrebné. - + Address is gone Adresa zmizla - + Bitmessage cannot find your address %1. Perhaps you removed it? Bitmessage nemôže nájsť vašu adresu %1. Možno ste ju odstránili? - + Address disabled Adresa deaktivovaná - + Error: The address from which you are trying to send is disabled. You'll have to enable it on the 'Your Identities' tab before using it. Chyba: adresa, z ktorej sa pokúšate odoslať, je neaktívna. Pred použitím ju musíte aktivovať v karte "Vaše identity". @@ -796,42 +796,42 @@ Tento súbor je dôležité zálohovať. Chcete tento súbor teraz otvoriť? (Ne - + Entry added to the blacklist. Edit the label to your liking. Záznam pridaný na zoznam zakázaných. Upravte označenie podľa vašich predstáv. - + Error: You cannot add the same address to your blacklist twice. Try renaming the existing one if you want. Chyba: tú istú adresu nemožno pridať na zoznam zakázaných dvakrát. Ak chcete, môžete skúsiť premenovať existujúce označenie. - + Moved items to trash. Položky presunuté do koša. - + Undeleted item. Položka obnovená. - + Save As... Uložiť ako... - + Write error. Chyba pri zapisovaní. - + No addresses selected. Nevybraná žiadna adresa. - + If you delete the subscription, messages that you already received will become inaccessible. Maybe you can consider disabling the subscription instead. Disabled subscriptions will not receive new messages, but you can still view messages you already received. Are you sure you want to delete the subscription? @@ -840,7 +840,7 @@ Are you sure you want to delete the subscription? Ste si istý, že chcete odber odstrániť? - + If you delete the channel, messages that you already received will become inaccessible. Maybe you can consider disabling the channel instead. Disabled channels will not receive new messages, but you can still view messages you already received. Are you sure you want to delete the channel? @@ -849,32 +849,32 @@ Are you sure you want to delete the channel? Ste si istý, že chcete kanál odstrániť? - + Do you really want to remove this avatar? Naozaj chcete odstrániť tento avatar? - + You have already set an avatar for this address. Do you really want to overwrite it? Pre túto adresu ste už ste nastavili avatar. Naozaj ho chcete ho zmeniť? - + Start-on-login not yet supported on your OS. Spustenie pri prihlásení zatiaľ pre váš operačný systém nie je podporované. - + Minimize-to-tray not yet supported on your OS. Minimalizovanie do panelu úloh zatiaľ pre váš operačný systém nie je podporované. - + Tray notifications not yet supported on your OS. Oblasť oznámení zatiaľ pre váš operačný systém nie je podporovaná. - + Testing... Testujem... @@ -939,192 +939,192 @@ Ste si istý, že chcete kanál odstrániť? - + Bitmessage Bitmessage - + Identities Identity - + New Identity Nová identita - + Search Hľadaj - + All Všetky - + To Príjemca - + From Odosielateľ - + Subject Predmet - + Message Správa - + Received Prijaté - + Messages Správy - + Address book Adresár - + Address Adresa - + Add Contact Pridať kontakt - + Fetch Namecoin ID Získať identifikátor namecoin-u - + Subject: Predmet: - + From: Odosielateľ: - + To: Príjemca: - + Send ordinary Message Poslať obyčajnú správu - + Send Message to your Subscribers Poslať správu vašim odberateľom - + TTL: Doba životnosti: - + Subscriptions Odbery - + Add new Subscription Pridať nový odber - + Chans Kanály - + Add Chan Pridať kanál - + File Súbor - + Settings Nastavenia - + Help Pomoc - + Import keys Importovať kľúče - + Manage keys Spravovať kľúče - + Ctrl+Q Ctrl+Q - + F1 F1 - + Contact support Kontaktovať používateľskú podporu - + About O - + Regenerate deterministic addresses Znova vytvoriť deterministické adresy - + Delete all trashed messages Odstrániť všetky správy z koša - + Join / Create chan Pripojiť / vytvoriť kanál - + All accounts Všetky účty @@ -1144,67 +1144,67 @@ Ste si istý, že chcete kanál odstrániť? Pridať nový záznam - + Display the %1 recent broadcast(s) from this address. - Zobraziť posledných %1 hromadných správ z tejto adresy. + - + New version of PyBitmessage is available: %1. Download it from https://github.com/Bitmessage/PyBitmessage/releases/latest K dispozícii je nová verzia PyBitmessage: %1. Môžete ju stiahnuť na https://github.com/Bitmessage/PyBitmessage/releases/latest - + Waiting for PoW to finish... %1% Čakám na ukončenie práce... %1% - + Shutting down Pybitmessage... %1% Ukončujem PyBitmessage... %1% - + Waiting for objects to be sent... %1% Čakám na odoslanie objektov... %1% - + Saving settings... %1% Ukladám nastavenia... %1% - + Shutting down core... %1% Ukončujem jadro... %1% - + Stopping notifications... %1% Zastavujem oznámenia... %1% - + Shutdown imminent... %1% Posledná fáza ukončenia... %1% - + %n hour(s) %n hodina%n hodiny%n hodín - + %n day(s) %n deň%n dni%n dní - + Shutting down PyBitmessage... %1% Ukončujem PyBitmessage... %1% - + Sent Odoslané @@ -1308,7 +1308,7 @@ Priímcova požadovaná obtiažnosť: %1 a %2 Problém: skúšate odslať správu sami sebe, ale nemôžem nájsť šifrovací kľúč v súbore keys.dat. Nemožno správu zašifrovať: %1 - + Doing work necessary to send message. Vykonávam prácu potrebnú na odoslanie... @@ -1318,7 +1318,7 @@ Priímcova požadovaná obtiažnosť: %1 a %2 Správa odoslaná. Čakanie na potvrdenie. Odoslaná %1 - + Doing work necessary to request encryption key. Vykonávam prácu potrebnú na vyžiadanie šifrovacieho kľúča. @@ -1343,37 +1343,37 @@ Priímcova požadovaná obtiažnosť: %1 a %2 Mapovanie portov UPnP zrušené - + Mark all messages as read Označiť všetky správy ako prečítané - + Are you sure you would like to mark all messages read? Ste si istý, že chcete označiť všetky správy ako prečítané? - + Doing work necessary to send broadcast. Vykonávam prácu potrebnú na rozoslanie. - + Proof of work pending Vykonávaná práca - + %n object(s) pending proof of work %n objekt čaká na vykonanie práce%n objekty čakajú na vykonanie práce%n objektov čaká na vykonanie práce - + %n object(s) waiting to be distributed %n objekt čaká na rozoslanie%n objekty čakajú na rozoslanie%n objektov čaká na rozoslanie - + Wait until these tasks finish? Počkať, kým tieto úlohy skončia? @@ -1438,12 +1438,12 @@ Priímcova požadovaná obtiažnosť: %1 a %2 Vaša grafická karta vykonala nesprávny výpočet, deaktivujem OpenCL. Prosím, pošlite správu vývojárom. - + Set notification sound... Nastaviť zvuk oznámenia... - + Welcome to easy and secure Bitmessage * send messages to other people @@ -1458,120 +1458,155 @@ Vitajte v jednoduchom a bezpečnom Bitmessage - + not recommended for chans nie je odporúčaná pre kanály - + Quiet Mode Tichý režim - + Problems connecting? Try enabling UPnP in the Network Settings Problémy so spojením? Skúste zapnúť UPnP v Nastaveniach siete - + You are trying to send an email instead of a bitmessage. This requires registering with a gateway. Attempt to register? Pokúšate sa odoslať e-mail namiesto bitmessage. To si vyžaduje registráciu na bráne. Pokúsiť sa o registráciu? - + Error: Bitmessage addresses start with BM- Please check the recipient address %1 Chyba: Bitmessage adresy začínajú s BM- Prosím skontrolujte adresu príjemcu %1 - + Error: The recipient address %1 is not typed or copied correctly. Please check it. Chyba: adresa príjemcu %1 nie je na správne napísaná alebo skopírovaná. Prosím skontrolujte ju. - + Error: The recipient address %1 contains invalid characters. Please check it. Chyba: adresa príjemcu %1 obsahuje neplatné znaky. Prosím skontrolujte ju. - + Error: The version of the recipient address %1 is too high. Either you need to upgrade your Bitmessage software or your acquaintance is being clever. Chyba: verzia adresy príjemcu %1 je príliš veľká. Buď musíte aktualizovať program Bitmessage alebo váš známy s vami žartuje. - + Error: Some data encoded in the recipient address %1 is too short. There might be something wrong with the software of your acquaintance. Chyba: niektoré údaje zakódované v adrese príjemcu %1 sú príliš krátke. Softér vášho známeho možno nefunguje správne. - + Error: Some data encoded in the recipient address %1 is too long. There might be something wrong with the software of your acquaintance. Chyba: niektoré údaje zakódované v adrese príjemcu %1 sú príliš dlhé. Softvér vášho známeho možno nefunguje správne. - + Error: Some data encoded in the recipient address %1 is malformed. There might be something wrong with the software of your acquaintance. Chyba: niektoré údaje zakódované v adrese príjemcu %1 sú poškodené. Softvér vášho známeho možno nefunguje správne. - + Error: Something is wrong with the recipient address %1. Chyba: niečo s adresou príjemcu %1 je nie je v poriadku. - + Error: %1 Chyba: %1 - + From %1 Od %1 - + Synchronisation pending Prebieha synchronizácia - + Bitmessage hasn't synchronised with the network, %n object(s) to be downloaded. If you quit now, it may cause delivery delays. Wait until the synchronisation finishes? Bitmessage sa nezosynchronizoval so sieťou, treba prevzať ešte %n objekt. Keď program ukončíte teraz, môže dôjsť k spozdeniu doručenia. Počkať, kým sa synchronizácia ukončí?Bitmessage sa nezosynchronizoval so sieťou, treba prevzať ešte %n objekty. Keď program ukončíte teraz, môže dôjsť k spozdeniu doručenia. Počkať, kým sa synchronizácia ukončí?Bitmessage sa nezosynchronizoval so sieťou, treba prevzať ešte %n objektov. Keď program ukončíte teraz, môže dôjsť k spozdeniu doručenia. Počkať, kým sa synchronizácia ukončí? - + Not connected Nepripojený - + Bitmessage isn't connected to the network. If you quit now, it may cause delivery delays. Wait until connected and the synchronisation finishes? Bitmessage nie je pripojený do siete. Keď program ukončíte teraz, môže dôjsť k spozdeniu doručenia. Počkať, kým dôjde k spojeniu a prebehne synchronizácia? - + Waiting for network connection... Čakám na pripojenie do siete... - + Waiting for finishing synchronisation... Čakám na ukončenie synchronizácie... - + You have already set a notification sound for this address book entry. Do you really want to overwrite it? Pre túto adresu ste už ste nastavili zvuk oznámenia. Naozaj ho chcete ho zmeniť? - + + Error occurred: could not load message from disk. + Chyba pri načítaní správy. + + + + Display the %n recent broadcast(s) from this address. + Zobraziť poslednú %1 hromadnú správu z tejto adresy.Zobraziť posledné %1 hromadné správy z tejto adresy.Zobraziť posledných %1 hromadných správ z tejto adresy. + + + Go online Pripojiť k sieti - + Go offline Odpojiť od siete + + + Clear + Vymazať + + + + inbox + Doručená pošta + + + + new + Nová doručená pošta + + + + sent + + + + + trash + + MessageView @@ -1762,7 +1797,7 @@ Predvoľba je pomocou generátora náhodných čísiel, ale deterministické adr Meno pseudo poštového zoznamu: - + This is a chan address. You cannot use it as a pseudo-mailing list. Toto je adresa kanálu. Nie je možné ju používať ako pseudo poštový zoznam. @@ -1970,27 +2005,27 @@ Predvoľba je pomocou generátora náhodných čísiel, ale deterministické adr - + Since startup on %1 Od spustenia %1 - + Down: %1/s Total: %2 Prijatých: %1/s Spolu: %2 - + Up: %1/s Total: %2 Odoslaných: %1/s Spolu: %2 - + Total Connections: %1 Spojení spolu: %1 - + Inventory lookups per second: %1 Vyhľadaní v inventári za sekundu: %1 @@ -2005,32 +2040,32 @@ Predvoľba je pomocou generátora náhodných čísiel, ale deterministické adr Prijatých: 0 kB/s - + Network Status Stav siete - + byte(s) bajtbajtybajtov - + Object(s) to be synced: %n Zostáva synchronizovať %n objektZostáva synchronizovať %n objektyZostáva synchronizovať %n objektov - + Processed %n person-to-person message(s). Spracovaná %n bežná správa.Spracované %n bežné správy.Spracovaných %n bežných správ. - + Processed %n broadcast message(s). Spracovaná %n hromadná správa.Spracované %n hromadné správy.Spracovaných %n hromadných správ. - + Processed %n public key(s). Spracovaný %n verejný kľúč.Spracované %n verejné kľúče.Spracovaných %n verejných kľúčov. @@ -2242,12 +2277,12 @@ Predvoľba je pomocou generátora náhodných čísiel, ale deterministické adr You must check (or not check) this box just like you did (or didn't) when you made your addresses the first time. - + Je nutné začiarknuť (alebo nezačiarknuť) toto políčko tak isto, ako keď ste vytvárali svoje adresy prvýkrát. If you have previously made deterministic addresses but lost them due to an accident (like hard drive failure), you can regenerate them here. If you used the random number generator to make your addresses then this form will be of no use to you. - + Ak ste v minulosti používali deterministické adresy, ale stratili ich kvôli nehode (ako je napráklad zlyhanie pevného disku), môžete ich vytvoriť znova. Ak ste na vytvorenie adries použili generátor náhodných čísel, potom je vám tento formulár zbytočný. From e5428ee0640bb2e4483b7f12643768f1f8be475e Mon Sep 17 00:00:00 2001 From: PyBitmessage Translations Date: Mon, 12 Feb 2018 21:50:45 +0100 Subject: [PATCH 398/407] Auto-updated language de from transifex --- src/translations/bitmessage_de.qm | Bin 96660 -> 97474 bytes src/translations/bitmessage_de.ts | 431 ++++++++++++++++-------------- 2 files changed, 233 insertions(+), 198 deletions(-) diff --git a/src/translations/bitmessage_de.qm b/src/translations/bitmessage_de.qm index 9fa26fae11ce88ebd486bcc7bd5f502ddad73991..ef443a619e0e40e55aede8479d45b488e57652f4 100644 GIT binary patch delta 3931 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 delta 3494 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 @mailchuck.com - + @mailchuck.com - + Registration failed: Registrierung fehlgeschlagen: - + The requested email address is not available, please try a new one. - + Die gewünschte E-Mailaddresse ist nicht verfügbar, bitte probieren Sie eine neue. - + Sending email gateway registration request Der Registrierungsantrag für die E-Mail Schnittstelle wird versandt. - + Sending email gateway unregistration request E-Mail Schnittestellen-Abmeldeantrag wird versandt - + Sending email gateway status request E-Mail Schnittestellen Statusantrag wird versandt @@ -203,52 +203,52 @@ Please type the desired email address (including @mailchuck.com) below: MainWindow - + Reply to sender Dem Absender antworten - + Reply to channel Antworten in den Chan - + Add sender to your Address Book Absender zum Adressbuch hinzufügen - + Add sender to your Blacklist Absender in die Blacklist eintragen - + Move to Trash In den Papierkorb verschieben - + Undelete Wiederherstellen - + View HTML code as formatted text HTML als formatierten Text anzeigen - + Save message as... Nachricht speichern unter... - + Mark Unread Als ungelesen markieren - + New Neu @@ -273,12 +273,12 @@ Please type the desired email address (including @mailchuck.com) below: Adresse in die Zwischenablage kopieren - + Special address behavior... Spezielles Verhalten der Adresse... - + Email gateway E-Mail Schnittstelle @@ -288,37 +288,37 @@ Please type the desired email address (including @mailchuck.com) below: Löschen - + Send message to this address Nachricht an diese Adresse senden - + Subscribe to this address Diese Adresse abonnieren - + Add New Address Neue Adresse hinzufügen - + Copy destination address to clipboard Zieladresse in die Zwischenablage kopieren - + Force send Senden erzwingen - + One of your addresses, %1, is an old version 1 address. Version 1 addresses are no longer supported. May we delete it now? Eine Ihrer Adressen, %1, ist eine alte Adresse der Version 1 und wird nicht mehr unterstützt. Soll sie jetzt gelöscht werden? - + Waiting for their encryption key. Will request it again soon. Warte auf den Verschlüsselungscode. Wird bald erneut angefordert. @@ -328,17 +328,17 @@ Please type the desired email address (including @mailchuck.com) below: - + Queued. In Warteschlange. - + Message sent. Waiting for acknowledgement. Sent at %1 Nachricht gesendet. Warte auf Bestätigung. Zeitpunkt der Sendung: %1 - + Message sent. Sent at %1 Nachricht gesendet. Zeitpunkt der Sendung: %1 @@ -348,77 +348,77 @@ Please type the desired email address (including @mailchuck.com) below: - + Acknowledgement of the message received %1 Bestätigung der Nachricht erhalten %1 - + Broadcast queued. Rundruf in Warteschlange. - + Broadcast on %1 Rundruf um %1 - + Problem: The work demanded by the recipient is more difficult than you are willing to do. %1 Problem: Die vom Empfänger geforderte Arbeit ist schwerer als Sie bereit sind, zu berechnen. %1 - + Problem: The recipient's encryption key is no good. Could not encrypt message. %1 Problem: Der Verschlüsselungscode des Empfängers ist nicht in Ordnung. Nachricht konnte nicht verschlüsselt werden. %1 - + Forced difficulty override. Send should start soon. Schwierigkeitslimit überschrieben. Senden sollte bald beginnen. - + Unknown status: %1 %2 Unbekannter Status: %1 %2 - + Not Connected Nicht verbunden - + Show Bitmessage Bitmessage anzeigen - + Send Senden - + Subscribe Abonnieren - + Channel Chan - + Quit Beenden - + You may manage your keys by editing the keys.dat file stored in the same directory as this program. It is important that you back up this file. Sie können Ihre Schlüssel verwalten, indem Sie die keys.dat bearbeiten, die im gleichen Ordner wie das Programm liegt. Es ist empfehlenswert, vorher ein Backup dieser Datei anzulegen. - + You may manage your keys by editing the keys.dat file stored in %1 It is important that you back up this file. @@ -427,17 +427,17 @@ It is important that you back up this file. Es ist empfehlenswert, vorher ein Backup dieser Datei anzulegen. - + Open keys.dat? Die keys.dat öffnen? - + You may manage your keys by editing the keys.dat file stored in the same directory as this program. It is important that you back up this file. Would you like to open the file now? (Be sure to close Bitmessage before making any changes.) Sie können Ihre Schlüssel verwalten, indem Sie die keys.dat bearbeiten, die im gleichen Ordner wie das Programm liegt. Es ist empfehlenswert, vorher ein Backup dieser Datei anzulegen. Möchten Sie die Datei jetzt öffnen? (Stellen Sie sicher, dass Sie Bitmessage beendet haben, bevor Sie etwas ändern.) - + You may manage your keys by editing the keys.dat file stored in %1 It is important that you back up this file. Would you like to open the file now? (Be sure to close Bitmessage before making any changes.) @@ -447,37 +447,37 @@ Es ist empfehlenswert, vorher ein Backup dieser Datei anzulegen. Möchten Sie di (Stellen Sie sicher, dass Sie Bitmessage beendet haben, bevor Sie etwas ändern.) - + Delete trash? Papierkorb leeren? - + Are you sure you want to delete all trashed messages? Sind Sie sicher, dass Sie alle Nachrichten im Papierkorb löschen möchten? - + bad passphrase Falsches Passwort - + You must type your passphrase. If you don't have one then this is not the form for you. Sie müssen Ihr Passwort eingeben. Wenn Sie keins haben, ist dies das falsche Formular für Sie. - + Bad address version number Falsche Addressenversionsnummer - + Your address version number must be a number: either 3 or 4. Die Addressenversionsnummer muss eine Zahl sein, entweder 3 oder 4. - + Your address version number must be either 3 or 4. Die Addressenversionnsnummer muss entweder 3 oder 4 sein. @@ -547,22 +547,22 @@ Es ist empfehlenswert, vorher ein Backup dieser Datei anzulegen. Möchten Sie di - + Connection lost Verbindung verloren - + Connected Verbunden - + Message trashed Nachricht in den Papierkorb verschoben - + The TTL, or Time-To-Live is the length of time that the network will hold the message. The recipient must get it during this time. If your Bitmessage client does not hear an acknowledgement, it will resend the message automatically. The longer the Time-To-Live, the @@ -570,17 +570,17 @@ Es ist empfehlenswert, vorher ein Backup dieser Datei anzulegen. Möchten Sie di Die Haltbarkeit, oder Time-To-Live, ist die Dauer, für die das Netzwerk die Nachricht speichern wird. Der Empfänger muss sie während dieser Zeit empfangen. Wenn Ihr Bitmessage-Client keine Empfangsbestätigung erhält, wird die Nachricht automatisch erneut verschickt. Je länger die Time-To-Live, desto mehr Arbeit muss Ihr Rechner verrichten, um die Nachricht zu senden. Eine Time-To-Live von vier oder fünf Tagen ist meist ausreichend. - + Message too long Narchricht zu lang - + The message that you are trying to send is too long by %1 bytes. (The maximum is 261644 bytes). Please cut it down before sending. Die Nachricht, die Sie zu senden versuchen, ist %1 Byte zu lang. (Maximum 261.644 Bytes). Bitte verringern Sie ihre Größe vor dem Senden. - + Error: Your account wasn't registered at an email gateway. Sending registration now as %1, please wait for the registration to be processed before retrying sending. Fehler: Ihr Konto war an keiner E-Mail Schnittstelle registriert. Registrierung als %1 wird versandt, bitte vor einem erneutem Sendeversuch auf die Registrierungsverarbeitung warten. @@ -625,57 +625,57 @@ Es ist empfehlenswert, vorher ein Backup dieser Datei anzulegen. Möchten Sie di - + Error: You must specify a From address. If you don't have one, go to the 'Your Identities' tab. Fehler: Sie müssen eine Absenderadresse auswählen. Sollten Sie keine haben, wechseln Sie zum Reiter "Ihre Identitäten". - + Address version number Adressversion - + Concerning the address %1, Bitmessage cannot understand address version numbers of %2. Perhaps upgrade Bitmessage to the latest version. Aufgrund der Adresse %1 kann Bitmessage Adressen mit der Version %2 nicht verarbeiten. Möglicherweise müssen Sie Bitmessage auf die aktuelle Version aktualisieren. - + Stream number Datenstrom Nummer - + Concerning the address %1, Bitmessage cannot handle stream numbers of %2. Perhaps upgrade Bitmessage to the latest version. Aufgrund der Adresse %1 kann Bitmessage den Datenstrom mit der Version %2 nicht verarbeiten. Möglicherweise müssen Sie Bitmessage auf die aktuelle Version aktualisieren. - + Warning: You are currently not connected. Bitmessage will do the work necessary to send the message but it won't send until you connect. Warnung: Sie sind aktuell nicht verbunden. Bitmessage wird die nötige Arbeit zum versenden verrichten, aber erst senden, wenn Sie verbunden sind. - + Message queued. Nachricht befindet sich in der Warteschleife. - + Your 'To' field is empty. Ihr "Empfänger"-Feld ist leer. - + Right click one or more entries in your address book and select 'Send message to this address'. Klicken Sie mit rechts auf einen oder mehrere Einträge aus Ihrem Adressbuch und wählen Sie "Nachricht an diese Adresse senden". - + Fetched address from namecoin identity. Adresse aus Namecoin Identität geholt. - + New Message Neue Nachricht @@ -700,47 +700,47 @@ Es ist empfehlenswert, vorher ein Backup dieser Datei anzulegen. Möchten Sie di Die von Ihnen eingegebene Adresse ist ungültig, sie wird ignoriert. - + Error: You cannot add the same address to your address book twice. Try renaming the existing one if you want. Fehler: Sie können eine Adresse nicht doppelt im Adressbuch speichern. Sie können jedoch die bereits eingetragene umbenennen. - + Error: You cannot add the same address to your subscriptions twice. Perhaps rename the existing one if you want. Fehler: Dieselbe Adresse kann nicht doppelt in die Abonnements eingetragen werden. Sie können jedoch die bereits eingetragene umbenennen. - + Restart Neustart - + You must restart Bitmessage for the port number change to take effect. Sie müssen Bitmessage neu starten, um den geänderten Port zu verwenden. - + Bitmessage will use your proxy from now on but you may want to manually restart Bitmessage now to close existing connections (if any). Bitmessage wird ab sofort den Proxy-Server verwenden, aber eventuell möchten Sie Bitmessage neu starten um bereits bestehende Verbindungen zu schließen. - + Number needed Zahl erforderlich - + Your maximum download and upload rate must be numbers. Ignoring what you typed. Ihre maximale Herungerlade- und Hochladegeschwindigkeit müssen Zahlen sein. Die eingetragenen Werte werden ignoriert. - + Will not resend ever Wird nie wiederversendet - + Note that the time limit you entered is less than the amount of time Bitmessage waits for the first resend attempt therefore your messages will never be resent. Bitte beachten Sie, dass der eingetratene Dauer kürzer ist als die, die Bitmessage auf das erste Wiederversenden wartet. Deswegen werden Ihre Nachrichten nie wiederversendet. @@ -775,22 +775,22 @@ Es ist empfehlenswert, vorher ein Backup dieser Datei anzulegen. Möchten Sie di Sie benötigen unbedingt ein Kennwort. - + Address is gone Adresse ist verloren - + Bitmessage cannot find your address %1. Perhaps you removed it? Bitmessage kann Ihre Adresse %1 nicht finden. Haben Sie sie gelöscht? - + Address disabled Adresse deaktiviert - + Error: The address from which you are trying to send is disabled. You'll have to enable it on the 'Your Identities' tab before using it. Fehler: Die Adresse von der Sie versuchen zu senden ist deaktiviert. Sie müssen sie unter dem Reiter "Ihre Identitäten" aktivieren bevor Sie fortfahren. @@ -800,42 +800,42 @@ Es ist empfehlenswert, vorher ein Backup dieser Datei anzulegen. Möchten Sie di - + Entry added to the blacklist. Edit the label to your liking. Eintrag in die Blacklist hinzugefügt. Die Beschriftung können Sie ändern. - + Error: You cannot add the same address to your blacklist twice. Try renaming the existing one if you want. Fehler: Dieselbe Addresse kann nicht doppelt in die Blacklist eingetragen werden. Sie können jedoch die bereits eingetragene umbenennen. - + Moved items to trash. Objekt(e) in den Papierkorb verschoben. - + Undeleted item. Nachricht wiederhergestellt. - + Save As... Speichern unter... - + Write error. Fehler beim Speichern. - + No addresses selected. Keine Adresse ausgewählt. - + If you delete the subscription, messages that you already received will become inaccessible. Maybe you can consider disabling the subscription instead. Disabled subscriptions will not receive new messages, but you can still view messages you already received. Are you sure you want to delete the subscription? @@ -844,7 +844,7 @@ Are you sure you want to delete the subscription? Sind Sie sicher, dass Sie das Abonnement löschen möchten? - + If you delete the channel, messages that you already received will become inaccessible. Maybe you can consider disabling the channel instead. Disabled channels will not receive new messages, but you can still view messages you already received. Are you sure you want to delete the channel? @@ -853,32 +853,32 @@ Are you sure you want to delete the channel? Sind Sie sicher, dass Sie das Chan löschen möchten? - + Do you really want to remove this avatar? Wollen Sie diesen Avatar wirklich entfernen? - + You have already set an avatar for this address. Do you really want to overwrite it? Sie haben bereits einen Avatar für diese Adresse gewählt. Wollen Sie ihn wirklich überschreiben? - + Start-on-login not yet supported on your OS. Mit Betriebssystem starten, noch nicht von Ihrem Betriebssystem unterstützt - + Minimize-to-tray not yet supported on your OS. Ins System Tray minimieren von Ihrem Betriebssytem noch nicht unterstützt. - + Tray notifications not yet supported on your OS. Trach-Benachrichtigungen von Ihrem Betriebssystem noch nicht unterstützt. - + Testing... teste... @@ -943,192 +943,192 @@ Sind Sie sicher, dass Sie das Chan löschen möchten? - + Bitmessage Bitmessage - + Identities Identitäten - + New Identity Neue Identität - + Search Suchen - + All Alle - + To An - + From Von - + Subject Betreff - + Message Nachricht - + Received Erhalten - + Messages Nachrichten - + Address book Addressbuch - + Address Adresse - + Add Contact Kontakt hinzufügen - + Fetch Namecoin ID Hole Namecoin ID - + Subject: Betreff: - + From: Von: - + To: An: - + Send ordinary Message Ordentliche Nachricht senden - + Send Message to your Subscribers Rundruf an Ihre Abonnenten senden - + TTL: Lebenszeit: - + Subscriptions Abonnements - + Add new Subscription Neues Abonnement anlegen - + Chans Chans - + Add Chan Chan hinzufügen - + File Datei - + Settings Einstellungen - + Help Hilfe - + Import keys Schlüssel importieren - + Manage keys Schlüssel verwalten - + Ctrl+Q Strg+Q - + F1 F1 - + Contact support Unterstützung anfordern - + About Über - + Regenerate deterministic addresses Deterministische Adressen neu generieren - + Delete all trashed messages Alle Nachrichten im Papierkorb löschen - + Join / Create chan Chan beitreten / erstellen - + All accounts Alle Identitäten @@ -1148,67 +1148,67 @@ Sind Sie sicher, dass Sie das Chan löschen möchten? Neuen Eintrag erstellen - + Display the %1 recent broadcast(s) from this address. - Die letzten %1 Rundruf(e) von dieser Addresse anzeigen. + - + New version of PyBitmessage is available: %1. Download it from https://github.com/Bitmessage/PyBitmessage/releases/latest Neue Version von PyBitmessage steht zur Verfügung: %1. Sie können sie von https://github.com/Bitmessage/PyBitmessage/releases/latest herunterladen. - + Waiting for PoW to finish... %1% Warte auf Abschluss von Berechnungen (PoW)... %1% - + Shutting down Pybitmessage... %1% PyBitmessage wird beendet... %1% - + Waiting for objects to be sent... %1% Warte auf Versand von Objekten... %1% - + Saving settings... %1% Einstellungen werden gespeichert... %1% - + Shutting down core... %1% Kern wird beendet... %1% - + Stopping notifications... %1% Beende Benachrichtigungen... %1% - + Shutdown imminent... %1% Unmittelbar vor Beendung... %1% - + %n hour(s) %n Stunde%n Stunden - + %n day(s) %n Tag%n Tage - + Shutting down PyBitmessage... %1% PyBitmessage wird beendet... %1% - + Sent Gesendet @@ -1311,7 +1311,7 @@ Receiver's required difficulty: %1 and %2 Problem: Sie versuchen, eine Nachricht an sich zu versenden, aber Ihr Schlüssel befindet sich nicht in der keys.dat-Datei. Die Nachricht kann nicht verschlüsselt werden. 1% - + Doing work necessary to send message. Arbeit wird verrichtet, um die Nachricht zu verschicken. @@ -1321,7 +1321,7 @@ Receiver's required difficulty: %1 and %2 Nachricht gesendet. Auf Bestätigung wird gewartet. Zeitpunkt der Sendung: %1 - + Doing work necessary to request encryption key. Arbeit wird verrichtet, um den Schlüssel nachzufragen... @@ -1346,37 +1346,37 @@ Receiver's required difficulty: %1 and %2 UPnP Port-Mapping entfernt - + Mark all messages as read Alle Nachrichten als gelesen markieren - + Are you sure you would like to mark all messages read? Sind Sie sicher, dass Sie alle Nachrichten als gelesen markieren möchten? - + Doing work necessary to send broadcast. Führe Arbeit aus, die notwendig ist zum Senden des Rundspruches. - + Proof of work pending Arbeitsbeweis wird berechnet - + %n object(s) pending proof of work %n Objekt wartet auf Berechnungen%n Objekte warten auf Berechnungen - + %n object(s) waiting to be distributed %n Objekt wartet darauf, verteilt zu werden%n Objekte warten darauf, verteilt zu werden - + Wait until these tasks finish? Warten bis diese Aufgaben erledigt sind? @@ -1441,12 +1441,12 @@ Receiver's required difficulty: %1 and %2 Ihre Grafikkarte hat inkorrekt berechnet, OpenCL wird deaktiviert. Bitte benachrichtigen Sie die Entwickler. - + Set notification sound... Benachrichtigungsklang einstellen ... - + Welcome to easy and secure Bitmessage * send messages to other people @@ -1460,120 +1460,155 @@ Willkommen zu einfachem und sicherem Bitmessage * diskutieren Sie mit anderen Leuten in Chans - + not recommended for chans für Chans nicht empfohlen - + Quiet Mode stiller Modus - + Problems connecting? Try enabling UPnP in the Network Settings Verbindungsprobleme? Versuchen Sie UPnP in den Netzwerkeinstellungen einzuschalten - + You are trying to send an email instead of a bitmessage. This requires registering with a gateway. Attempt to register? Sie versuchen, eine E-Mail anstelle einer Bitmessage zu senden. Dies erfordert eine Registrierung bei einer Schnittstelle. Registrierung versuchen? - + Error: Bitmessage addresses start with BM- Please check the recipient address %1 Fehler: Bitmessage Adressen starten mit BM- Bitte überprüfen Sie die Empfängeradresse %1 - + Error: The recipient address %1 is not typed or copied correctly. Please check it. Fehler: Die Empfängeradresse %1 wurde nicht korrekt getippt oder kopiert. Bitte überprüfen. - + Error: The recipient address %1 contains invalid characters. Please check it. Fehler: Die Empfängeradresse %1 beinhaltet ungültig Zeichen. Bitte überprüfen. - + Error: The version of the recipient address %1 is too high. Either you need to upgrade your Bitmessage software or your acquaintance is being clever. Fehler: Die Empfängerdresseversion von %1 ist zu hoch. Entweder Sie müssen Ihre Bitmessage Software aktualisieren oder Ihr Bekannter ist sehr clever. - + Error: Some data encoded in the recipient address %1 is too short. There might be something wrong with the software of your acquaintance. Fehler: Einige Daten die in der Empfängerdresse %1 codiert sind, sind zu kurz. Es könnte sein, dass etwas mit der Software Ihres Bekannten nicht stimmt. - + Error: Some data encoded in the recipient address %1 is too long. There might be something wrong with the software of your acquaintance. Fehler: Einige Daten die in der Empfängeradresse %1 codiert sind, sind zu lang. Es könnte sein, dass etwas mit der Software Ihres Bekannten nicht stimmt. - + Error: Some data encoded in the recipient address %1 is malformed. There might be something wrong with the software of your acquaintance. Fehler: Einige codierte Daten in der Empfängeradresse %1 sind ungültig. Es könnte etwas mit der Software Ihres Bekannten sein. - + Error: Something is wrong with the recipient address %1. Fehler: Mit der Empfängeradresse %1 stimmt etwas nicht. - + Error: %1 Fehler: %1 - + From %1 Von %1 - + Synchronisation pending Synchronisierung läuft - + Bitmessage hasn't synchronised with the network, %n object(s) to be downloaded. If you quit now, it may cause delivery delays. Wait until the synchronisation finishes? Bitmessage ist nicht synchronisiert, %n Objekt wurde noch nicht heruntergeladen. Wenn Sie das Programm jetzt beenden, kann es zu Zustellverzögerungen kommen. Warten bis die Synchronisierung abgeschlossen ist?Bitmessage ist nicht synchronisiert, %n Objekte wurden noch nicht heruntergeladen. Wenn Sie das Programm jetzt beenden, kann es zu Zustellverzögerungen kommen. Warten bis die Synchronisierung abgeschlossen ist? - + Not connected Nicht verbunden - + Bitmessage isn't connected to the network. If you quit now, it may cause delivery delays. Wait until connected and the synchronisation finishes? Bitmessage ist nicht mit dem Netzwerk verbunden. Wenn Sie das Programm jetzt beenden, kann es zu Zustellverzögerungen kommen. Warten bis eine Verbindung besteht und die Synchronisierung abgeschlossen ist? - + Waiting for network connection... Warte auf Netzwerkverbindung... - + Waiting for finishing synchronisation... Warte auf Synchronisationsabschluss... - + You have already set a notification sound for this address book entry. Do you really want to overwrite it? Sie haben bereits einen Benachrichtigungsklang für diesen Adressbucheintrag gesetzt. Möchten Sie ihn wirklich überschreiben? - + + Error occurred: could not load message from disk. + Fehler: Nachricht konnte nicht geladen werden. + + + + Display the %n recent broadcast(s) from this address. + Den letzten %1 Rundruf von dieser Addresse anzeigen.Die letzten %1 Rundrufe von dieser Addresse anzeigen. + + + Go online Mit dem Netzwerk verbinden - + Go offline Trennen vom Netzwerk + + + Clear + Löschen + + + + inbox + Eingang + + + + new + Neu + + + + sent + + + + + trash + + MessageView @@ -1764,7 +1799,7 @@ Die Zufallszahlen-Option ist standardmässig gewählt, jedoch haben deterministi Name der Pseudo-Mailingliste: - + This is a chan address. You cannot use it as a pseudo-mailing list. Dies ist eine Chan-Adresse. Sie können sie nicht als Pseudo-Mailingliste verwenden. @@ -1972,27 +2007,27 @@ Die Zufallszahlen-Option ist standardmässig gewählt, jedoch haben deterministi - + Since startup on %1 Seit Start der Anwendung am %1 - + Down: %1/s Total: %2 Herunter: %1/s Insg.: %2 - + Up: %1/s Total: %2 Hoch: %1/s Insg.: %2 - + Total Connections: %1 Verbindungen insgesamt: %1 - + Inventory lookups per second: %1 Inventory lookups pro Sekunde: %1 @@ -2007,32 +2042,32 @@ Die Zufallszahlen-Option ist standardmässig gewählt, jedoch haben deterministi Herunter: 0 kB/s - + Network Status Netzwerkstatus - + byte(s) ByteBytes - + Object(s) to be synced: %n %n Objekt zu synchronisieren.%n Objekte zu synchronisieren. - + Processed %n person-to-person message(s). %n Person-zu-Person-Nachricht bearbeitet.%n Person-zu-Person-Nachrichten bearbeitet. - + Processed %n broadcast message(s). %n Rundruf-Nachricht bearbeitet.%n Rundruf-Nachrichten bearbeitet. - + Processed %n public key(s). %n öffentlicher Schlüssel verarbeitet.%n öffentliche Schlüssel verarbeitet. From 4ba5702cc22fa8b52e3b7dba3f9df44b1f28b197 Mon Sep 17 00:00:00 2001 From: PyBitmessage Translations Date: Mon, 12 Feb 2018 22:31:54 +0100 Subject: [PATCH 399/407] Auto-updated language ru from transifex --- src/translations/bitmessage_ru.qm | Bin 91386 -> 92911 bytes src/translations/bitmessage_ru.ts | 446 ++++++++++++++++-------------- 2 files changed, 243 insertions(+), 203 deletions(-) diff --git a/src/translations/bitmessage_ru.qm b/src/translations/bitmessage_ru.qm index c831d389433262f7920f31b7e6fd20c4e0ba7586..53d58f012d8e96cf2653679d120850ea71268188 100644 GIT binary patch delta 4794 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 delta 3587 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 UibYtKjO)q%=1TPL>~W9(0fyBrLI3~& diff --git a/src/translations/bitmessage_ru.ts b/src/translations/bitmessage_ru.ts index 38e1112f..ffb2eac9 100644 --- a/src/translations/bitmessage_ru.ts +++ b/src/translations/bitmessage_ru.ts @@ -60,27 +60,27 @@ @mailchuck.com - + Registration failed: Регистрация не удалась: - + The requested email address is not available, please try a new one. Запрашиваемый адрес email недоступен, попробуйте ввести другой. - + Sending email gateway registration request Отправка запроса на регистрацию на Email-шлюзе - + Sending email gateway unregistration request Отправка запроса на отмену регистрации на Email-шлюзе - + Sending email gateway status request Отправка запроса статуса аккаунта на Email-шлюзе @@ -197,52 +197,52 @@ Please type the desired email address (including @mailchuck.com) below: MainWindow - + Reply to sender Ответить отправителю - + Reply to channel Ответить в канал - + Add sender to your Address Book Добавить отправителя в адресную книгу - + Add sender to your Blacklist Добавить отправителя в чёрный список - + Move to Trash Поместить в корзину - + Undelete Отменить удаление - + View HTML code as formatted text Просмотреть HTML код как отформатированный текст - + Save message as... Сохранить сообщение как ... - + Mark Unread Отметить как непрочитанное - + New Новый адрес @@ -267,12 +267,12 @@ Please type the desired email address (including @mailchuck.com) below: Скопировать адрес в буфер обмена - + Special address behavior... Особое поведение адресов... - + Email gateway Email-шлюз @@ -282,37 +282,37 @@ Please type the desired email address (including @mailchuck.com) below: Удалить - + Send message to this address Отправить сообщение на этот адрес - + Subscribe to this address Подписаться на рассылку с этого адреса - + Add New Address Добавить новый адрес - + Copy destination address to clipboard Скопировать адрес отправки в буфер обмена - + Force send Форсировать отправку - + One of your addresses, %1, is an old version 1 address. Version 1 addresses are no longer supported. May we delete it now? Один из Ваших адресов, %1, является устаревшим адресом версии 1. Адреса версии 1 больше не поддерживаются. Хотите ли Вы удалить его сейчас? - + Waiting for their encryption key. Will request it again soon. Ожидаем ключ шифрования от Вашего собеседника. Запрос будет повторен через некоторое время. @@ -322,17 +322,17 @@ Please type the desired email address (including @mailchuck.com) below: - + Queued. В очереди. - + Message sent. Waiting for acknowledgement. Sent at %1 Сообщение отправлено. Ожидаем подтверждения. Отправлено в %1 - + Message sent. Sent at %1 Сообщение отправлено в %1 @@ -342,78 +342,78 @@ Please type the desired email address (including @mailchuck.com) below: - + Acknowledgement of the message received %1 Доставлено в %1 - + Broadcast queued. Рассылка ожидает очереди. - + Broadcast on %1 Рассылка на %1 - + Problem: The work demanded by the recipient is more difficult than you are willing to do. %1 Проблема: Ваш получатель требует более сложных вычислений, чем максимум, указанный в Ваших настройках. %1 - + Problem: The recipient's encryption key is no good. Could not encrypt message. %1 Проблема: ключ получателя неправильный. Невозможно зашифровать сообщение. %1 - + Forced difficulty override. Send should start soon. Форсирована смена сложности. Отправляем через некоторое время. - + Unknown status: %1 %2 Неизвестный статус: %1 %2 - + Not Connected Не соединено - + Show Bitmessage Показать Bitmessage - + Send Отправить - + Subscribe Подписки - + Channel Канал - + Quit Выйти - + You may manage your keys by editing the keys.dat file stored in the same directory as this program. It is important that you back up this file. Вы можете управлять Вашими ключами, редактируя файл keys.dat, находящийся в той же папке, что и эта программа. Создайте резервную копию этого файла перед тем как будете его редактировать. - + You may manage your keys by editing the keys.dat file stored in %1 It is important that you back up this file. @@ -422,19 +422,19 @@ It is important that you back up this file. Создайте резервную копию этого файла перед тем как будете его редактировать. - + Open keys.dat? Открыть файл keys.dat? - + You may manage your keys by editing the keys.dat file stored in the same directory as this program. It is important that you back up this file. Would you like to open the file now? (Be sure to close Bitmessage before making any changes.) Вы можете управлять Вашими ключами, редактируя файл keys.dat, находящийся в той же папке, что и эта программа. Создайте резервную копию этого файла перед тем как будете его редактировать. Хотели бы Вы открыть этот файл сейчас? (пожалуйста, закройте Bitmessage до того как Вы внесёте в этот файл какие-либо изменения.) - + You may manage your keys by editing the keys.dat file stored in %1 It is important that you back up this file. Would you like to open the file now? (Be sure to close Bitmessage before making any changes.) @@ -444,37 +444,37 @@ It is important that you back up this file. Would you like to open the file now? (пожалуйста, закройте Bitmessage до того как Вы внесёте в этот файл какие-либо изменения.) - + Delete trash? Очистить корзину? - + Are you sure you want to delete all trashed messages? Вы уверены что хотите очистить корзину? - + bad passphrase Неподходящая секретная фраза - + You must type your passphrase. If you don't have one then this is not the form for you. Вы должны ввести секретную фразу. Если Вы не хотите этого делать, то Вы выбрали неправильную опцию. - + Bad address version number Неверный номер версии адреса - + Your address version number must be a number: either 3 or 4. Адрес номера версии должен быть числом: либо 3, либо 4. - + Your address version number must be either 3 or 4. Адрес номера версии должен быть либо 3, либо 4. @@ -544,22 +544,22 @@ It is important that you back up this file. Would you like to open the file now? - + Connection lost Соединение потеряно - + Connected Соединено - + Message trashed Сообщение удалено - + The TTL, or Time-To-Live is the length of time that the network will hold the message. The recipient must get it during this time. If your Bitmessage client does not hear an acknowledgement, it will resend the message automatically. The longer the Time-To-Live, the @@ -570,17 +570,17 @@ It is important that you back up this file. Would you like to open the file now? сообщение. Часто разумным вариантом будет установка TTL на 4 или 5 дней. - + Message too long Сообщение слишком длинное - + The message that you are trying to send is too long by %1 bytes. (The maximum is 261644 bytes). Please cut it down before sending. Сообщение, которое вы пытаетесь отправить, длиннее максимально допустимого на %1 байт. (Максимально допустимое значение 261644 байта). Пожалуйста, сократите сообщение перед отправкой. - + Error: Your account wasn't registered at an email gateway. Sending registration now as %1, please wait for the registration to be processed before retrying sending. Ошибка: ваш аккаунт не зарегистрирован на Email-шлюзе. Отправка регистрации %1, пожалуйста, подождите пока процесс регистрации не завершится, прежде чем попытаться отправить сообщение заново. @@ -625,57 +625,57 @@ It is important that you back up this file. Would you like to open the file now? - + Error: You must specify a From address. If you don't have one, go to the 'Your Identities' tab. Вы должны указать адрес в поле "От кого". Вы можете найти Ваш адрес во вкладке "Ваши Адреса". - + Address version number Версия адреса - + Concerning the address %1, Bitmessage cannot understand address version numbers of %2. Perhaps upgrade Bitmessage to the latest version. По поводу адреса %1: Bitmessage не поддерживаем адреса версии %2. Возможно, Вам нужно обновить клиент Bitmessage. - + Stream number Номер потока - + Concerning the address %1, Bitmessage cannot handle stream numbers of %2. Perhaps upgrade Bitmessage to the latest version. По поводу адреса %1: Bitmessage не поддерживаем стрим номер %2. Возможно, Вам нужно обновить клиент Bitmessage. - + Warning: You are currently not connected. Bitmessage will do the work necessary to send the message but it won't send until you connect. Внимание: Вы не подключены к сети. Bitmessage выполнит работу, требуемую для отправки сообщения, но не отправит его до тех пор, пока Вы не подключитесь. - + Message queued. Сообщение в очереди. - + Your 'To' field is empty. Вы не заполнили поле 'Кому'. - + Right click one or more entries in your address book and select 'Send message to this address'. Нажмите правую кнопку мыши на каком-либо адресе и выберите "Отправить сообщение на этот адрес". - + Fetched address from namecoin identity. Получить адрес через Namecoin. - + New Message Новое сообщение @@ -700,47 +700,47 @@ It is important that you back up this file. Would you like to open the file now? Вы ввели неправильный адрес. Это адрес проигнорирован. - + Error: You cannot add the same address to your address book twice. Try renaming the existing one if you want. Ошибка: Вы не можете добавлять один и тот же адрес в Адресную Книгу несколько раз. Попробуйте переименовать существующий адрес. - + Error: You cannot add the same address to your subscriptions twice. Perhaps rename the existing one if you want. Ошибка: вы не можете добавить один и тот же адрес в ваши подписки дважды. Пожалуйста, переименуйте имеющийся адрес, если хотите. - + Restart Перезапустить - + You must restart Bitmessage for the port number change to take effect. Вы должны перезапустить Bitmessage, чтобы смена номера порта имела эффект. - + Bitmessage will use your proxy from now on but you may want to manually restart Bitmessage now to close existing connections (if any). Bitmessage будет использовать Ваш прокси, начиная прямо сейчас. Тем не менее Вам имеет смысл перезапустить Bitmessage, чтобы закрыть уже существующие соединения. - + Number needed Требуется число - + Your maximum download and upload rate must be numbers. Ignoring what you typed. Скорости загрузки и выгрузки должны быть числами. Игнорирую то, что вы набрали. - + Will not resend ever Не пересылать никогда - + Note that the time limit you entered is less than the amount of time Bitmessage waits for the first resend attempt therefore your messages will never be resent. Обратите внимание, что лимит времени, который вы ввели, меньше чем время, которое Bitmessage ждет перед первой попыткой переотправки сообщения, поэтому ваши сообщения никогда не будут переотправлены. @@ -775,22 +775,22 @@ It is important that you back up this file. Would you like to open the file now? Вы действительно должны ввести секретную фразу. - + Address is gone Адрес утерян - + Bitmessage cannot find your address %1. Perhaps you removed it? Bitmessage не может найти Ваш адрес %1. Возможно Вы удалили его? - + Address disabled Адрес выключен - + Error: The address from which you are trying to send is disabled. You'll have to enable it on the 'Your Identities' tab before using it. Ошибка: адрес, с которого Вы пытаетесь отправить, выключен. Вам нужно будет включить этот адрес во вкладке "Ваши адреса". @@ -800,42 +800,42 @@ It is important that you back up this file. Would you like to open the file now? - + Entry added to the blacklist. Edit the label to your liking. Запись добавлена в чёрный список. Измените название по своему вкусу. - + Error: You cannot add the same address to your blacklist twice. Try renaming the existing one if you want. Ошибка: вы не можете добавить один и тот же адрес в чёрный список дважды. Попробуйте переименовать существующий адрес. - + Moved items to trash. Удалено в корзину. - + Undeleted item. Элемент восстановлен. - + Save As... Сохранить как ... - + Write error. Ошибка записи. - + No addresses selected. Вы не выбрали адрес. - + If you delete the subscription, messages that you already received will become inaccessible. Maybe you can consider disabling the subscription instead. Disabled subscriptions will not receive new messages, but you can still view messages you already received. Are you sure you want to delete the subscription? @@ -844,7 +844,7 @@ Are you sure you want to delete the subscription? Вы уверены, что хотите отменить подписку? - + If you delete the channel, messages that you already received will become inaccessible. Maybe you can consider disabling the channel instead. Disabled channels will not receive new messages, but you can still view messages you already received. Are you sure you want to delete the channel? @@ -853,32 +853,32 @@ Are you sure you want to delete the channel? Вы уверены, что хотите удалить канал? - + Do you really want to remove this avatar? Вы уверены, что хотите удалить этот аватар? - + You have already set an avatar for this address. Do you really want to overwrite it? У вас уже есть аватар для этого адреса. Вы уверены, что хотите перезаписать аватар? - + Start-on-login not yet supported on your OS. Запуск программы при входе в систему ещё не поддерживается в вашей операционной системе. - + Minimize-to-tray not yet supported on your OS. Сворачивание в трей ещё не поддерживается в вашей операционной системе. - + Tray notifications not yet supported on your OS. Уведомления в трее ещё не поддерживаеются в вашей операционной системе. - + Testing... Проверяем... @@ -943,192 +943,192 @@ Are you sure you want to delete the channel? - + Bitmessage Bitmessage - + Identities Адреса - + New Identity Создать новый адрес - + Search Поиск - + All По всем полям - + To Кому - + From От кого - + Subject Тема - + Message Текст сообщения - + Received Получено - + Messages Сообщения - + Address book Адресная книга - + Address Адрес - + Add Contact Добавить контакт - + Fetch Namecoin ID Получить Namecoin ID - + Subject: Тема: - + From: От: - + To: Кому: - + Send ordinary Message Отправить обычное сообщение - + Send Message to your Subscribers Отправить сообщение для ваших подписчиков - + TTL: TTL: - + Subscriptions Подписки - + Add new Subscription Добавить новую подписку - + Chans Чаны - + Add Chan Добавить чан - + File Файл - + Settings Настройки - + Help Помощь - + Import keys Импортировать ключи - + Manage keys Управлять ключами - + Ctrl+Q Ctrl+Q - + F1 F1 - + Contact support Связаться с поддержкой - + About О программе - + Regenerate deterministic addresses Сгенерировать заново все адреса - + Delete all trashed messages Стереть все сообщения из корзины - + Join / Create chan Подключить или создать чан - + All accounts Все аккаунты @@ -1148,67 +1148,67 @@ Are you sure you want to delete the channel? Добавить новую запись - + Display the %1 recent broadcast(s) from this address. - Показать %1 прошлых рассылок с этого адреса. + - + New version of PyBitmessage is available: %1. Download it from https://github.com/Bitmessage/PyBitmessage/releases/latest Доступна новая версия PyBitmessage: %1. Загрузите её: https://github.com/Bitmessage/PyBitmessage/releases/latest - + Waiting for PoW to finish... %1% Ожидание окончания PoW... %1% - + Shutting down Pybitmessage... %1% Завершение PyBitmessage... %1% - + Waiting for objects to be sent... %1% Ожидание отправки объектов... %1% - + Saving settings... %1% Сохранение настроек... %1% - + Shutting down core... %1% Завершение работы ядра... %1% - + Stopping notifications... %1% Остановка сервиса уведомлений... %1% - + Shutdown imminent... %1% Завершение вот-вот произойдет... %1% - + %n hour(s) %n час%n часа%n часов%n час(а/ов) - + %n day(s) %n день%n дня%n дней%n дней - + Shutting down PyBitmessage... %1% Завершение PyBitmessage... %1% - + Sent Отправлено @@ -1312,7 +1312,7 @@ Receiver's required difficulty: %1 and %2 Проблема: вы пытаетесь отправить сообщение самому себе или в чан, но ваш ключ шифрования не найден в файле ключей keys.dat. Невозможно зашифровать сообщение. %1 - + Doing work necessary to send message. Выполнение работы, требуемой для отправки сообщения. @@ -1322,7 +1322,7 @@ Receiver's required difficulty: %1 and %2 Отправлено. Ожидаем подтверждения. Отправлено в %1 - + Doing work necessary to request encryption key. Выполнение работы, требуемой для запроса ключа шифрования. @@ -1347,37 +1347,37 @@ Receiver's required difficulty: %1 and %2 Распределение портов UPnP отменено - + Mark all messages as read Отметить все сообщения как прочтенные - + Are you sure you would like to mark all messages read? Вы уверены, что хотите отметить все сообщения как прочтенные? - + Doing work necessary to send broadcast. Выполнение работы, требуемой для отправки рассылки. - + Proof of work pending Ожидается доказательство работы - + %n object(s) pending proof of work %n объект в ожидании доказательства работы%n объекта в ожидании доказательства работы%n объектов в ожидании доказательства работы%n объектов в ожидании доказательства работы - + %n object(s) waiting to be distributed %n объект ожидает раздачи%n объекта ожидают раздачи%n объектов ожидают раздачи%n объектов ожидают раздачи - + Wait until these tasks finish? Подождать завершения этих задач? @@ -1442,12 +1442,12 @@ Receiver's required difficulty: %1 and %2 Ваша видеокарта вычислила неправильно, отключаем OpenCL. Пожалуйста, сообщите разработчикам. - + Set notification sound... Установить звук уведомления... - + Welcome to easy and secure Bitmessage * send messages to other people @@ -1461,115 +1461,155 @@ Receiver's required difficulty: %1 and %2 * участвуйте в обсуждениях в чанах - + not recommended for chans не рекомендовано для чанов - + Quiet Mode Тихий режим - + Problems connecting? Try enabling UPnP in the Network Settings Проблемы подключения? Попробуйте включить UPnP в сетевых настройках. - + You are trying to send an email instead of a bitmessage. This requires registering with a gateway. Attempt to register? Вы пытаетесь отправить email вместо bitmessage. Для этого нужно зарегистрироваться на шлюзе. Попробовать зарегистрироваться? - + Error: Bitmessage addresses start with BM- Please check the recipient address %1 Ошибка: адреса Bitmessage начинаются с "BM-". Пожалуйста, проверьте адрес получателя %1. - + Error: The recipient address %1 is not typed or copied correctly. Please check it. Ошибка: адрес получателя %1 набран или скопирован неправильно. Пожалуйста, проверьте его. - + Error: The recipient address %1 contains invalid characters. Please check it. Ошибка: адрес получателя %1 содержит недопустимые символы. Пожалуйста, проверьте его. - + Error: The version of the recipient address %1 is too high. Either you need to upgrade your Bitmessage software or your acquaintance is being clever. Ошибка: версия адреса получателя %1 слишком высокая. Либо вам нужно обновить программу Bitmessage, либо ваш знакомый - умник. - + Error: Some data encoded in the recipient address %1 is too short. There might be something wrong with the software of your acquaintance. Ошибка: часть данных, закодированных в адресе получателя %1 слишком короткая. Видимо, что-то не так с программой, используемой вашим знакомым. - + Error: Some data encoded in the recipient address %1 is too long. There might be something wrong with the software of your acquaintance. Ошибка: часть данных, закодированных в адресе получателя %1 слишком длинная. Видимо, что-то не так с программой, используемой вашим знакомым. - + Error: Some data encoded in the recipient address %1 is malformed. There might be something wrong with the software of your acquaintance. Ошибка: часть данных, закодированных в адресе получателя %1 сформирована неправильно. Видимо, что-то не так с программой, используемой вашим знакомым. - + Error: Something is wrong with the recipient address %1. Ошибка: что-то не так с адресом получателя %1. - + + Error: %1 + Ошибка: %1 + + + From %1 От %1 - + Synchronisation pending Ожидается синхронизация - + Bitmessage hasn't synchronised with the network, %n object(s) to be downloaded. If you quit now, it may cause delivery delays. Wait until the synchronisation finishes? Bitmessage не синхронизирован с сетью, незагруженных объектов: %n. Выход сейчас может привести к задержкам доставки. Подождать завершения синхронизации?Bitmessage не синхронизирован с сетью, незагруженных объектов: %n. Выход сейчас может привести к задержкам доставки. Подождать завершения синхронизации?Bitmessage не синхронизирован с сетью, незагруженных объектов: %n. Выход сейчас может привести к задержкам доставки. Подождать завершения синхронизации?Bitmessage не синхронизирован с сетью, незагруженных объектов: %n. Выход сейчас может привести к задержкам доставки. Подождать завершения синхронизации? - + Not connected Не подключено - + Bitmessage isn't connected to the network. If you quit now, it may cause delivery delays. Wait until connected and the synchronisation finishes? Bitmessage не подключен к сети. Выход сейчас может привести к задержкам доставки. Подождать подключения и завершения синхронизации? - + Waiting for network connection... Ожидание сетевого подключения... - + Waiting for finishing synchronisation... Ожидание окончания синхронизации... - + You have already set a notification sound for this address book entry. Do you really want to overwrite it? У вас уже есть звук уведомления для этого адресата. Вы уверены, что хотите перезаписать звук уведомления? - + + Error occurred: could not load message from disk. + Произошла ошибка: не удалось загрузить сообщение с диска. + + + + Display the %n recent broadcast(s) from this address. + + + + Go online Подключиться к сети - + Go offline Отключиться от сети + + + Clear + Очистить + + + + inbox + входящие + + + + new + новые + + + + sent + отправленные + + + + trash + + MessageView @@ -1759,7 +1799,7 @@ The 'Random Number' option is selected by default but deterministic ad Имя псевдо-рассылки: - + This is a chan address. You cannot use it as a pseudo-mailing list. Это адрес чана. Вы не можете его использовать как адрес рассылки. @@ -1782,12 +1822,12 @@ The 'Random Number' option is selected by default but deterministic ad - + <html><head/><body><p>Distributed under the MIT/X11 software license; see <a href="http://www.opensource.org/licenses/mit-license.php"><span style=" text-decoration: underline; color:#0000ff;">http://www.opensource.org/licenses/mit-license.php</span></a></p></body></html> <html><head/><body><p>Программа распространяется в соответствии с лицензией MIT/X11; см. <a href="http://www.opensource.org/licenses/mit-license.php"><span style=" text-decoration: underline; color:#0000ff;">http://www.opensource.org/licenses/mit-license.php</span></a></p></body></html> - + This is Beta software. Это бета версия программы. @@ -1797,7 +1837,7 @@ The 'Random Number' option is selected by default but deterministic ad - + <html><head/><body><p>Copyright © 2012-2016 Jonathan Warren<br/>Copyright © 2013-2017 The Bitmessage Developers</p></body></html> <html><head/><body><p>Авторское право: &copy; 2012-2016 Джонатан Уоррен<br/>Авторское право: &copy; 2013-2017 Разработчики Bitmessage</p></body></html> @@ -1967,27 +2007,27 @@ The 'Random Number' option is selected by default but deterministic ad - + Since startup on %1 С начала работы, %1 - + Down: %1/s Total: %2 Загрузка: %1/s Всего: %2 - + Up: %1/s Total: %2 Отправка: %1/s Всего: %2 - + Total Connections: %1 Всего соединений: %1 - + Inventory lookups per second: %1 Поисков в каталоге в секунду: %1 @@ -2002,34 +2042,34 @@ The 'Random Number' option is selected by default but deterministic ad Загрузка: 0 кБ/с - + Network Status Состояние сети - + byte(s) байтбайтбайтбайт - + Object(s) to be synced: %n Несинхронизированные объекты: %nНесинхронизированные объекты: %nНесинхронизированные объекты: %nНесинхронизированные объекты: %n - + Processed %n person-to-person message(s). - Обработано %n сообщение.Обработано %n сообщений.Обработано %n сообщений.Обработано %n сообщений. + Обработано %n сообщение.Обработано %n сообщения.Обработано %n сообщений.Обработано %n сообщений. - + Processed %n broadcast message(s). - Обработана %n рассылка.Обработано %n рассылок.Обработано %n рассылок.Обработано %n рассылок. + Обработана %n рассылка.Обработано %n рассылки.Обработано %n рассылок.Обработано %n рассылок. - + Processed %n public key(s). - Обработан %n открытый ключ.Обработано %n открытых ключей.Обработано %n открытых ключей.Обработано %n открытых ключей. + Обработан %n открытый ключ.Обработано %n открытых ключа.Обработано %n открытых ключей.Обработано %n открытых ключей. @@ -2239,12 +2279,12 @@ The 'Random Number' option is selected by default but deterministic ad You must check (or not check) this box just like you did (or didn't) when you made your addresses the first time. - + Вы должны отметить эту галочку (или не отмечать) точно так же, как Вы сделали (или не сделали) в самый первый раз, когда создавали Ваши адреса. If you have previously made deterministic addresses but lost them due to an accident (like hard drive failure), you can regenerate them here. If you used the random number generator to make your addresses then this form will be of no use to you. - + Если Вы ранее создали детерминистические адреса, но случайно потеряли их, Вы можете их восстановить здесь. Если же Вы использовали генератор случайных чисел чтобы создать Ваши адреса, то Вы не сможете их здесь восстановить. From 8b932ade2d26d0ee3b5d4b69b4ea096bc588b4fa Mon Sep 17 00:00:00 2001 From: Dmitri Bogomolov <4glitch@gmail.com> Date: Tue, 13 Feb 2018 00:50:47 +0200 Subject: [PATCH 400/407] No "getinfo" in modern namecoind - try "getnetworkinfo" --- src/namecoin.py | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/src/namecoin.py b/src/namecoin.py index 2cfa5a1d..1124bbeb 100644 --- a/src/namecoin.py +++ b/src/namecoin.py @@ -129,12 +129,14 @@ class namecoinConnection (object): # Test the connection settings. This routine tries to query a "getinfo" # command, and builds either an error message or a success message with # some info from it. - def test (self): + def test(self): try: if self.nmctype == "namecoind": - res = self.callRPC ("getinfo", []) - vers = res["version"] - + try: + vers = self.callRPC("getinfo", [])["version"] + except RPCError: + vers = self.callRPC("getnetworkinfo", [])["version"] + v3 = vers % 100 vers = vers / 100 v2 = vers % 100 From 3ad94cb4aa0366019295d6a32cc498fc0a18dbe9 Mon Sep 17 00:00:00 2001 From: Dmitri Bogomolov <4glitch@gmail.com> Date: Tue, 13 Feb 2018 11:53:43 +0200 Subject: [PATCH 401/407] Translate namecoin failure message --- src/namecoin.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/namecoin.py b/src/namecoin.py index 1124bbeb..9b3c3c3e 100644 --- a/src/namecoin.py +++ b/src/namecoin.py @@ -162,7 +162,11 @@ class namecoinConnection (object): except Exception: logger.info("Namecoin connection test failure") - return ('failed', "The connection to namecoin failed.") + return ( + 'failed', + tr._translate( + "MainWindow", "The connection to namecoin failed.") + ) # Helper routine that actually performs an JSON RPC call. def callRPC (self, method, params): From f9a648d720848a8027ba2cb684da35681417973f Mon Sep 17 00:00:00 2001 From: Peter Surda Date: Tue, 13 Feb 2018 13:24:37 +0100 Subject: [PATCH 402/407] Message decoding exception handler fix - was unfinished and caused the object processor thread to crash --- src/class_objectProcessor.py | 10 ++++++++-- src/helper_msgcoding.py | 26 +++++++++++++++++--------- 2 files changed, 25 insertions(+), 11 deletions(-) diff --git a/src/class_objectProcessor.py b/src/class_objectProcessor.py index 6387f6a7..181ce30e 100644 --- a/src/class_objectProcessor.py +++ b/src/class_objectProcessor.py @@ -509,7 +509,10 @@ class objectProcessor(threading.Thread): if toLabel == '': toLabel = toAddress - decodedMessage = helper_msgcoding.MsgDecode(messageEncodingType, message) + try: + decodedMessage = helper_msgcoding.MsgDecode(messageEncodingType, message) + except helper_msgcoding.MsgDecodeException: + return subject = decodedMessage.subject body = decodedMessage.body @@ -761,7 +764,10 @@ class objectProcessor(threading.Thread): sendersAddressVersion, sendersStream, calculatedRipe) logger.debug('fromAddress: ' + fromAddress) - decodedMessage = helper_msgcoding.MsgDecode(messageEncodingType, message) + try: + decodedMessage = helper_msgcoding.MsgDecode(messageEncodingType, message) + except helper_msgcoding.MsgDecodeException: + return subject = decodedMessage.subject body = decodedMessage.body diff --git a/src/helper_msgcoding.py b/src/helper_msgcoding.py index aae35d27..f8bc95a6 100644 --- a/src/helper_msgcoding.py +++ b/src/helper_msgcoding.py @@ -21,7 +21,15 @@ BITMESSAGE_ENCODING_SIMPLE = 2 BITMESSAGE_ENCODING_EXTENDED = 3 -class DecompressionSizeException(Exception): +class MsgEncodeException(Exception): + pass + + +class MsgDecodeException(Exception): + pass + + +class DecompressionSizeException(MsgDecodeException): def __init__(self, size): self.size = size @@ -38,7 +46,7 @@ class MsgEncode(object): elif self.encoding == BITMESSAGE_ENCODING_TRIVIAL: self.encodeTrivial(message) else: - raise ValueError("Unknown encoding %i" % (encoding)) + raise MsgEncodeException("Unknown encoding %i" % (encoding)) def encodeExtended(self, message): try: @@ -46,10 +54,10 @@ class MsgEncode(object): self.data = zlib.compress(msgpack.dumps(msgObj.encode(message)), 9) except zlib.error: logger.error("Error compressing message") - raise + raise MsgEncodeException("Error compressing message") except msgpack.exceptions.PackException: logger.error("Error msgpacking message") - raise + raise MsgEncodeException("Error msgpacking message") self.length = len(self.data) def encodeSimple(self, message): @@ -85,7 +93,7 @@ class MsgDecode(object): data = dc.unconsumed_tail except zlib.error: logger.error("Error decompressing message") - raise + raise MsgDecodeException("Error decompressing message") else: raise DecompressionSizeException(len(tmp)) @@ -94,21 +102,21 @@ class MsgDecode(object): except (msgpack.exceptions.UnpackException, msgpack.exceptions.ExtraData): logger.error("Error msgunpacking message") - raise + raise MsgDecodeException("Error msgunpacking message") try: msgType = tmp[""] except KeyError: logger.error("Message type missing") - raise + raise MsgDecodeException("Message type missing") msgObj = messagetypes.constructObject(tmp) if msgObj is None: - raise ValueError("Malformed message") + raise MsgDecodeException("Malformed message") try: msgObj.process() except: - raise ValueError("Malformed message") + raise MsgDecodeException("Malformed message") if msgType == "message": self.subject = msgObj.subject self.body = msgObj.body From 96ea36cfd245f7dc10209b01278b5fa2970f360c Mon Sep 17 00:00:00 2001 From: Peter Surda Date: Tue, 13 Feb 2018 16:11:53 +0100 Subject: [PATCH 403/407] UPnP client port randomize --- src/upnp.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/upnp.py b/src/upnp.py index ff657619..ad72560c 100644 --- a/src/upnp.py +++ b/src/upnp.py @@ -187,7 +187,7 @@ class uPnPThread(threading.Thread, StoppableThread): self.localIP = self.getLocalIP() self.routers = [] self.sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) - self.sock.bind((self.localIP, 10000)) + self.sock.bind((self.localIP, 0)) self.sock.setsockopt(socket.IPPROTO_IP, socket.IP_MULTICAST_TTL, 2) self.sock.settimeout(5) self.sendSleep = 60 From 3a8016d31f517775d226aa8b902480f4a3a148a9 Mon Sep 17 00:00:00 2001 From: Peter Surda Date: Tue, 13 Feb 2018 16:39:35 +0100 Subject: [PATCH 404/407] Fix message encoding bug - prevent loading invalid message types --- src/messagetypes/__init__.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/messagetypes/__init__.py b/src/messagetypes/__init__.py index c3911dfd..d9291013 100644 --- a/src/messagetypes/__init__.py +++ b/src/messagetypes/__init__.py @@ -12,9 +12,10 @@ class MsgBase(object): def constructObject(data): try: - classBase = eval(data[""] + "." + data[""].title()) - except NameError: - logger.error("Don't know how to handle message type: \"%s\"", data[""]) + m = import_module("messagetypes." + data[""]) + classBase = getattr(m, data[""].title()) + except (NameError, ImportError): + logger.error("Don't know how to handle message type: \"%s\"", data[""], exc_info=True) return None try: returnObj = classBase() From 4cd36ececc5ec467526a9d084ea8818125dbedd1 Mon Sep 17 00:00:00 2001 From: Peter Surda Date: Tue, 13 Feb 2018 17:16:20 +0100 Subject: [PATCH 405/407] Bump version --- src/version.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/version.py b/src/version.py index 694f71e4..a3837aa5 100644 --- a/src/version.py +++ b/src/version.py @@ -1,2 +1,2 @@ softwareName = 'PyBitmessage' -softwareVersion = '0.6.2' +softwareVersion = '0.6.3' From c050ef0814b920cb92f66a632046d084b31292e9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Peter=20=C5=A0urda?= Date: Tue, 13 Feb 2018 23:33:12 +0100 Subject: [PATCH 406/407] Messagetype attack mitigation - temporarily restrict messagetypes - use a new "Contact support" address --- src/bitmessageqt/support.py | 4 +++- src/messagetypes/__init__.py | 3 +++ 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/src/bitmessageqt/support.py b/src/bitmessageqt/support.py index cea5ddc8..25c6113d 100644 --- a/src/bitmessageqt/support.py +++ b/src/bitmessageqt/support.py @@ -21,7 +21,8 @@ import state from version import softwareVersion # this is BM support address going to Peter Surda -SUPPORT_ADDRESS = 'BM-2cTkCtMYkrSPwFTpgcBrMrf5d8oZwvMZWK' +OLD_SUPPORT_ADDRESS = 'BM-2cTkCtMYkrSPwFTpgcBrMrf5d8oZwvMZWK' +SUPPORT_ADDRESS = 'BM-2cUdgkDDAahwPAU6oD2A7DnjqZz3hgY832' SUPPORT_LABEL = 'PyBitmessage support' SUPPORT_MY_LABEL = 'My new address' SUPPORT_SUBJECT = 'Support request' @@ -53,6 +54,7 @@ Connected hosts: {} ''' def checkAddressBook(myapp): + sqlExecute('''DELETE from addressbook WHERE address=?''', OLD_SUPPORT_ADDRESS) queryreturn = sqlQuery('''SELECT * FROM addressbook WHERE address=?''', SUPPORT_ADDRESS) if queryreturn == []: sqlExecute('''INSERT INTO addressbook VALUES (?,?)''', str(QtGui.QApplication.translate("Support", SUPPORT_LABEL)), SUPPORT_ADDRESS) diff --git a/src/messagetypes/__init__.py b/src/messagetypes/__init__.py index d9291013..1a5223df 100644 --- a/src/messagetypes/__init__.py +++ b/src/messagetypes/__init__.py @@ -11,6 +11,9 @@ class MsgBase(object): def constructObject(data): + whitelist = ["message"] + if data[""] not in whitelist: + return None try: m = import_module("messagetypes." + data[""]) classBase = getattr(m, data[""].title()) From 634a49cd6d8fc2f52504586be4c4766340641b25 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Peter=20=C5=A0urda?= Date: Wed, 14 Feb 2018 00:23:47 +0100 Subject: [PATCH 407/407] Bump version --- src/version.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/version.py b/src/version.py index a3837aa5..076b8c56 100644 --- a/src/version.py +++ b/src/version.py @@ -1,2 +1,2 @@ softwareName = 'PyBitmessage' -softwareVersion = '0.6.3' +softwareVersion = '0.6.3.2'