Переглянути джерело

Merge branch 'experimental' into testing

Karsten Böddeker 6 роки тому
батько
коміт
7c4393c3f1
7 змінених файлів з 153 додано та 194 видалено
  1. 66 83
      extra/template.conf
  2. 32 41
      i18n/de.po
  3. 32 14
      i18n/en.po
  4. 9 0
      i18n/gluon-site.pot
  5. 3 3
      modules
  6. 0 33
      patches/0001-openwrt-patch-iw.patch
  7. 11 20
      site.mk

+ 66 - 83
extra/template.conf

@@ -24,9 +24,9 @@
 
 	-- OpenWRT Package Repository (IPv6 capable)
 	opkg = {
-		openwrt = 'http://firmware.srv.in.ffho.net/openwrt/%n/%v/%S/packages',
+		lede = 'http://firmware.srv.in.ffho.net/lede/packages-%v/%A',
 		extra = {
-			modules = 'http://firmware.srv.in.ffho.net/%GR/modules/%S',
+			gluon = 'http://firmware.srv.in.ffho.net/%GR/modules/%S',
 		},
 	},
 
@@ -67,78 +67,79 @@
 	next_node = {
 		ip4 = '%N4',
 		ip6 = '2a03:2260:2342:%V6::1',
+		name = 'node.ffho.net',
 
 		-- mac address, must be unique in the client network
 		-- (usually, you don't need to change this)
 		mac = 'f2:ff:ff:%ID:ff:ff',
 	},
 
-	-- fastd (vpn) settings
-	fastd_mesh_vpn = {
-		-- cipher(s) to use
-		methods = {'salsa2012+umac'},
-
+	mesh_vpn = {
 		-- the maximum transfer unit
 		mtu = 1406,
 
-		groups = {
-			backbone = {
-				-- specifies how many servers are contacted
-				-- by a client simultanously
-				limit = 1,
-
-				-- list all available vpn servers
-				peers = {
-					gw01 = {
-						-- public fastd key of the vpn server
-						key = '97bc56ae561f67cfe15364134868c3553b5f1eab06f7bd9c5b93fbf280a52b51',
-						-- remotes is an array of ways on how to reach the VPN server
-						-- Use public addresses here, these addresses are used when the mesh might not be connected yet.
-						-- format: '[|ipv4|ipv6] "hostname-or-ip-address" port 10000'
-						remotes = {
-							'"gw01.ffho.net" port 100%ID',
-							'ipv6 "2001:830:c:9::12" port 100%ID',
-							'ipv4 "80.242.130.70" port 100%ID',
+		-- fastd (vpn) settings
+		fastd = {
+			-- cipher(s) to use
+			methods = {'salsa2012+umac'},
+
+			groups = {
+				backbone = {
+					-- specifies how many servers are contacted
+					-- by a client simultanously
+					limit = 1,
+
+					-- list all available vpn servers
+					peers = {
+						gw01 = {
+							-- public fastd key of the vpn server
+							key = '97bc56ae561f67cfe15364134868c3553b5f1eab06f7bd9c5b93fbf280a52b51',
+							-- remotes is an array of ways on how to reach the VPN server
+							-- Use public addresses here, these addresses are used when the mesh might not be connected yet.
+							-- format: '[|ipv4|ipv6] "hostname-or-ip-address" port 10000'
+							remotes = {
+								'"gw01.ffho.net" port 100%ID',
+								'ipv6 "2001:830:c:9::12" port 100%ID',
+								'ipv4 "80.242.130.70" port 100%ID',
+							},
 						},
-					},
-					gw02 = {
-						key = 'e8fa9bf402fd33dd1ba804a97f2e46c704423ff49e9c55312845a7bfb93121ad',
-						remotes = {
-							'"gw02.ffho.net" port 100%ID',
-							'ipv6 "2a01:4f8:190:6500::18:1" port 100%ID',
-							'ipv4 "78.46.242.24" port 100%ID',
+						gw02 = {
+							key = 'e8fa9bf402fd33dd1ba804a97f2e46c704423ff49e9c55312845a7bfb93121ad',
+							remotes = {
+								'"gw02.ffho.net" port 100%ID',
+								'ipv6 "2a01:4f8:190:6500::18:1" port 100%ID',
+								'ipv4 "78.46.242.24" port 100%ID',
+							},
 						},
-					},
-					gw03 = {
-						key = '629b2742922f068ef49558283aea4b75c1427d8cdd3fcd2dbd7c00880547d3b3',
-						remotes = {
-							'"gw03.ffho.net" port 100%ID',
-							'ipv6 "2a00:13c8:1000:2::165" port 100%ID',
-							'ipv4 "185.46.137.165" port 100%ID',
+						gw03 = {
+							key = '629b2742922f068ef49558283aea4b75c1427d8cdd3fcd2dbd7c00880547d3b3',
+							remotes = {
+								'"gw03.ffho.net" port 100%ID',
+								'ipv6 "2a00:13c8:1000:2::165" port 100%ID',
+								'ipv4 "185.46.137.165" port 100%ID',
+							},
 						},
-					},
-					gw04 = {
-						key = 'cf3855e5fa04e4c04a294cb6e8902f91c7ff7ef82bf031cc842a2b79b936677c',
-						remotes = {
-							'"gw04.ffho.net" port 100%ID',
-							'ipv6 "2a02:450:1::22" port 100%ID',
-							'ipv4 "80.70.181.63" port 100%ID',
+						gw04 = {
+							key = 'cf3855e5fa04e4c04a294cb6e8902f91c7ff7ef82bf031cc842a2b79b936677c',
+							remotes = {
+								'"gw04.ffho.net" port 100%ID',
+								'ipv6 "2a02:450:1::22" port 100%ID',
+								'ipv4 "80.70.181.63" port 100%ID',
+							},
 						},
-					},
-					gw05 = {
-						key = 'd0d32a2f536a0fc1f0c848e6da15bd96851d5ff4fdf9239c830bab033ddcec1a',
-						remotes = {
-							'"gw05.ffho.net" port 100%ID',
-							'ipv6 "2001:638:502:1884::ff15" port 100%ID',
-							'ipv4 "192.26.175.183" port 100%ID',
+						gw05 = {
+							key = 'd0d32a2f536a0fc1f0c848e6da15bd96851d5ff4fdf9239c830bab033ddcec1a',
+							remotes = {
+								'"gw05.ffho.net" port 100%ID',
+								'ipv6 "2001:638:502:1884::ff15" port 100%ID',
+								'ipv4 "192.26.175.183" port 100%ID',
+							},
 						},
-					},
-					gw06 = {
-						key = '88b570c4cc811259b7c4106943098c9bee1126d692ddacedd23cd48efc28b0bf',
-						remotes = {
-							'"gw06.ffho.net" port 100%ID',
-							'ipv6 "2001:41d0:a:377a::54" port 100%ID',
-							'ipv4 "5.196.106.54" port 100%ID',
+						gw06 = {
+							key = '88b570c4cc811259b7c4106943098c9bee1126d692ddacedd23cd48efc28b0bf',
+							remotes = {
+								'"gw06.ffho.net" port 100%ID',
+							},
 						},
 					},
 				},
@@ -208,37 +209,19 @@
 		},
 	},
 
-	-- configure ffho-debug
-	debugserver = {
-		host = { 'debugreport.ffho.net', '2a01:a700:4621:d102::10' },
-		port = 1337,
-	},
-
-	-- enable BATMAN on WAN interface by default (requires gluon-batman-on-wan package)
-	batman_on_wan = 0,
-
 	config_mode = {
+		-- Show/hide the altitude field
 		geo_location = {
 			show_altitude = false,
 		},
+		-- define if the contact field is obligatory (optional)
 		owner = {
 			obligatory = false,
 		},
 	},
 
-	-- link to map
-	status_page = {
-		location_link = 'https://map.hochstift.freifunk.net/#/de/map/',
-	},
-
-	-- geo-default site
-	site_select = {
-		geo_default_site = 'ffho_yho',
-	},
-
-	ath9k_workaround = {
-		blackout_wait = 60,
-		reset_wait = 300,
-		step_size = 10,
+	-- configure DNS forwarding
+	dns = {
+		servers = { '2a03:2260:2342:f251::53' },
 	},
 }

+ 32 - 41
i18n/de.po

@@ -2,7 +2,7 @@ msgid ""
 msgstr ""
 "Content-Type: text/plain; charset=UTF-8\n"
 "Project-Id-Version: PACKAGE VERSION\n"
-"PO-Revision-Date: 2015-11-02 13:15+0100\n"
+"PO-Revision-Date: 2017-06-25 11:55+0200\n"
 "Last-Translator: Freifunk Hochstift <kontakt@hochstift.freifunk.net>\n"
 "Language-Team: German\n"
 "Language: de\n"
@@ -18,57 +18,48 @@ msgstr ""
 
 msgid "gluon-config-mode:site-select"
 msgstr ""
-"Hier hast du die Möglichkeit die Region, in der sich dein "
+"Hier hast du die Möglichkeit, die Region, in der sich dein "
 "Knoten befindet, auszuwählen. Bitte denke daran, dass dein Router "
 "sich nur mit dem Netz der ausgewählten Region verbindet."
 
 msgid "gluon-config-mode:pubkey"
 msgstr ""
-"Dies ist der öffentliche Schlüssel deines Freifunk-Knotens. Erst nachdem "
+"<p>Dies ist der öffentliche Schlüssel deines Freifunk-Knotens. Erst nachdem "
 "er auf den Servern des Freifunk Hochstift-Projektes eingetragen wurde, "
 "kann sich dein Knoten mit dem Mesh-VPN verbinden. Bitte "
 "schicke dazu diesen Schlüssel und den Namen deines Knotens "
-"an <a href=\"mailto:kontakt@hochstift.freifunk.net\">kontakt@hochstift.freifunk.net</a> oder benutze am besten gleich das "
-"<a href=\"http://hochstift.freifunk.net/?page_id=366&key=<%=urlescape(pubkey)%>&nname=<%=urlescape(hostname)%>&mac=<%=urlescape(sysconfig.primary_mac)%>&email=<%=urlescape(contact or '')%>\"> "
-"vorausgefüllte Formular auf unserer Webseite</a>.<br><br>"
+"an <a href=\"mailto:kontakt@hochstift.freifunk.net?subject=<%= urlencode('new node \'' .. hostname .. '\'') %>&amp;"
+"body=<%= urlencode(hostname .. '\n\n# Owner: ') %><%= urlencode(contact or '\n') %><%= urlencode('\n# MAC: ' .. sysconfig.primary_mac .. '\nkey \"' .. pubkey .. '\";\n\nDeine Nachricht:\n') %>\">kontakt@hochstift.freifunk.net</a> oder benutze das "
+"<a href=\"https://hochstift.freifunk.net/neuen-knoten-eintragen/?key=<%= pubkey %>&amp;nname=<%= urlencode(hostname) %>&amp;mac=<%= urlencode(sysconfig.primary_mac) %>&amp;email=<%= urlencode(contact or '') %>\"> "
+"vorausgefüllte Formular auf unserer Webseite</a>."
 "</p>"
 "<div class=\"the-key\">"
-"<%=escape(hostname)%><br/><br/>"
-"# Owner: <%=escape(contact or '')%><br/>"
-"# MAC: <%=escape(sysconfig.primary_mac)%><br/>"
-"key \"<%=escape(pubkey)%>\";"
+"<%= pcdata(hostname) %><br/><br/>"
+"# Owner: <%= pcdata(contact) %><br/>"
+"# MAC: <%= pcdata(sysconfig.primary_mac) %><br/>"
+"key \"<%= pubkey %>\";"
 "</div>"
 
+msgid "gluon-config-mode:novpn"
+msgstr ""
+"<p>Du hast ausgewählt, kein Mesh-VPN zu nutzen. "
+"Dein Knoten kann also nur dann eine Verbindung zum Freifunk-Netz "
+"aufbauen, wenn andere Freifunk-Router in WLAN-Reichweite sind."
+"</p>"
+
 msgid "gluon-config-mode:reboot"
 msgstr ""
-"<p> "
-"Dein Knoten startet gerade neu und wird anschließend versuchen, "
-"sich anschließend mit anderen Freifunkknoten in seiner Nähe zu "
-"verbinden. Weitere Informationen zur  "
-"Freifunk Hochstift-Community findest du auf  "
-"<a href=\"http://hochstift.freifunk.net/\">unserer Webseite</a>. "
-"</p> "
-"<p> "
-"Viel Spaß mit deinem Knoten und der Erkundung von Freifunk! "
-"</p> "
-"<p> "
-"Abschließend möchten wir dir noch die Nutzungsbedingungen ans  "
-"Herz legen. Bitte beachte sie. "
-"</p> "
-"<h1>Nutzungsbedingunen</h1> "
-"Freifunk ist komplett von seinen Benutzern geschaffen worden, es gibt keine  "
-"mächtige Instanz, die über das Netz herrscht. Jeder Anwender ist zum gleichen  "
-"Teil an dem Projekt beteiligt. Die Beteiligten betreiben das Netzwerk auf  "
-"eigene Kosten und dies ist nur möglich, wenn sich alle an ein paar Regeln  "
-"halten:<br><br> "
-"<b>Sei Fair!</b><br> "
-"Das Netzwerk darf nicht in einer Weise beansprucht werden die das Netzwerk  "
-"selbst, dessen Betreibende oder andere Teilnehmende beeinträchtigt.<br><br> "
-"<b>Achte auf deine Sicherheit!</b><br> "
-"Das Netzwerk ist, wie das Internet, unverschlüsselt und offen. Jede  "
-"teilnehmende Person ist selbst für die Sicherheit ihrer Verbindungen und  "
-"ihrer Endgeräte verantwortlich.<br><br> "
-"<b>Keine rechtswidrige Nutzung!</b><br> "
-"Das Netzwerk darf nicht für Handlungen missbraucht werden, welche gesetzliche "
-"Bestimmungen oder die Rechte Dritter verletzen. Es dürfen keine Inhalte "
-"übertragen werden, welche gegen geltendes Recht verstoßen. "
+"<p>Dein Knoten startet gerade neu und wird anschließend versuchen, "
+"sich mit anderen Freifunkknoten in seiner Nähe zu verbinden. "
+"Weitere Informationen zur Freifunk Hochstift-Community findest du auf  "
+"<a href=\"https://hochstift.freifunk.net/\">unserer Webseite</a>."
+"</p>"
+"<p>Viel Spaß mit deinem Knoten und der Erkundung von Freifunk!</p>"
+
+msgid "gluon-config-mode:altitude-label"
+msgstr "Höhe"
+
+msgid "gluon-config-mode:altitude-help"
+msgstr ""
+"Die Höhenangabe ist optional und sollte nur gesetzt werden, wenn ein "
+"exakter Wert bekannt ist."

+ 32 - 14
i18n/en.po

@@ -2,7 +2,7 @@ msgid ""
 msgstr ""
 "Content-Type: text/plain; charset=UTF-8\n"
 "Project-Id-Version: PACKAGE VERSION\n"
-"PO-Revision-Date: 2015-11-02 13:15+0100\n"
+"PO-Revision-Date: 2017-06-25 12:20+0200\n"
 "Last-Translator: Freifunk Hochstift <kontakt@hochstift.freifunk.net>\n"
 "Language-Team: English\n"
 "Language: en\n"
@@ -23,25 +23,43 @@ msgstr ""
 
 msgid "gluon-config-mode:pubkey"
 msgstr ""
-"This is your Freifunk node's public key. The node won't be able to "
+"<p>This is your Freifunk node's public key. The node won't be able to "
 "connect to the mesh VPN until the key has been registered on the Freifunk "
 "Hochstift servers. "
-"To register the key fill out "
-"<a href=\"http://hochstift.freifunk.net/?page_id=366&key=<%=urlescape(pubkey)%>&nname=<%=urlescape(hostname)%>&mac=<%=urlescape(sysconfig.primary_mac)%>&email=<%=urlescape(contact or '')%>\">this form</a> "
-"or send it together with your node's name (<em><%=escape(hostname)%></em>) to "
-"<a href=\"mailto:kontakt@hochstift.freifunk.net\">kontakt@hochstift.freifunk.net</a>."
+"To register, send the key together with your node's name to "
+"<a href=\"mailto:kontakt@hochstift.freifunk.net?subject=<%= urlencode('new node \'' .. hostname .. '\'') %>&amp;"
+"body=<%= urlencode(hostname .. '\n\n# Owner: ') %><%= urlencode(contact or '\n') %><%= urlencode('\n# MAC: ' .. sysconfig.primary_mac .. '\nkey \"' .. pubkey .. '\";\n\nYour message:\n') %>\">kontakt@hochstift.freifunk.net</a> or use the "
+"<a href=\"https://hochstift.freifunk.net/neuen-knoten-eintragen/?key=<%= pubkey %>&amp;nname=<%= urlencode(hostname) %>&amp;mac=<%= urlencode(sysconfig.primary_mac) %>&amp;email=<%= urlencode(contact or '') %>\"> "
+"formular filled in beforehand on our website</a>."
 "</p>"
 "<div class=\"the-key\">"
-"<%=escape(hostname)%><br/><br/>"
-"# Owner: <%=escape(contact or '')%><br/>"
-"# MAC: <%=escape(sysconfig.primary_mac)%><br/>"
-"key \"<%=escape(pubkey)%>\";"
+"<%= pcdata(hostname) %><br/><br/>"
+"# Owner: <%= pcdata(contact) %><br/>"
+"# MAC: <%= pcdata(sysconfig.primary_mac) %><br/>"
+"key \"<%= pubkey %>\";"
 "</div>"
 
+msgid "gluon-config-mode:novpn"
+msgstr ""
+"<p>You have selected not to use the mesh VPN. "
+"Your node will only be able to connect to the Freifunk network if other nodes in reach "
+"already have a connection.</p>"
+
 msgid "gluon-config-mode:reboot"
 msgstr ""
-"<p>The node is currently rebooting and will try to connect to other "
-"nearby Freifunk nodes  after that. "
-"Your can find lots of information on the Freifunk Hochstift community on "
+"<p>Your node <em><%= pcdata(hostname) %></em> is currently rebooting and will "
+"try to connect to other nearby Freifunk nodes after that. For more "
+"information about the Freifunk Hochstift community, have a look at "
 "<a href=\"https://hochstift.freifunk.net/\">our homepage</a>.</p>"
-"<p>Have fun with your node and exploring the Freifunk network!</p>"
+"<p>To get back to this configuration interface, press the reset button for "
+"3 seconds during normal operation. The device will then reboot into config "
+"mode.</p>"
+"<p>Have fun with your node and exploring of the Freifunk network!</p>"
+
+msgid "gluon-config-mode:altitude-label"
+msgstr "Altitude"
+
+msgid "gluon-config-mode:altitude-help"
+msgstr ""
+"Specifying the altitude is optional and should only be done if a proper "
+"value is known.

+ 9 - 0
i18n/gluon-site.pot

@@ -10,5 +10,14 @@ msgstr ""
 msgid "gluon-config-mode:pubkey"
 msgstr ""
 
+msgid "gluon-config-mode:novpn"
+msgstr ""
+
 msgid "gluon-config-mode:reboot"
 msgstr ""
+
+msgid "gluon-config-mode:altitude-label"
+msgstr ""
+
+msgid "gluon-config-mode:altitude-help"
+msgstr ""

+ 3 - 3
modules

@@ -1,5 +1,5 @@
 GLUON_SITE_FEEDS='ffho'
 
-PACKAGES_FFHO_REPO=https://git.ffho.net/FreifunkHochstift/ffho-packages.git
-PACKAGES_FFHO_BRANCH=v2016.2.x
-PACKAGES_FFHO_COMMIT=72ba236324fb538dc304695285fae059489365b9
+PACKAGES_FFHO_REPO=https://git.ffho.net/freifunkhochstift/ffho-packages.git
+PACKAGES_FFHO_BRANCH=v2017.1.x
+PACKAGES_FFHO_COMMIT=806f98a8f4bc0896ba29b903a8b9f72c39db2f09

+ 0 - 33
patches/0001-openwrt-patch-iw.patch

@@ -1,33 +0,0 @@
-From: Karsten Böddeker <freifunk@kb-light.de>
-Date: Sat, 23 Apr 2016 15:20:30 +0200
-Subject: openwrt: patch iw
-
-diff --git a/patches/openwrt/1001-iw-patch-200-reduce-size-patch.patch b/patches/openwrt/1001-iw-patch-200-reduce-size-patch.patch
-new file mode 100644
-index 0000000..09c978e
---- /dev/null
-+++ b/patches/openwrt/1001-iw-patch-200-reduce-size-patch.patch
-@@ -0,0 +1,23 @@
-+From: Karsten Böddeker <freifunk@kb-light.de>
-+Date: Sat, 23 Apr 2016 15:20:29 +0200
-+Subject: iw: patch 200-reduce_size.patch
-+
-+diff --git a/package/network/utils/iw/patches/200-reduce_size.patch b/package/network/utils/iw/patches/200-reduce_size.patch
-+index dea24fb..fefbf1f 100644
-+--- a/package/network/utils/iw/patches/200-reduce_size.patch
-++++ b/package/network/utils/iw/patches/200-reduce_size.patch
-+@@ -1,12 +1,11 @@
-+ --- a/Makefile
-+ +++ b/Makefile
-+-@@ -15,8 +15,8 @@ CFLAGS += -Wall -Wundef -Wstrict-prototy
-++@@ -16,7 +16,7 @@ CFLAGS += -Wall -Wundef -Wstrict-prototy
-+  OBJS = iw.o genl.o event.o info.o phy.o \
-+  	interface.o ibss.o station.o survey.o util.o ocb.o \
-+  	mesh.o mpath.o mpp.o scan.o reg.o version.o \
-+--	reason.o status.o connect.o link.o offch.o ps.o cqm.o \
-++ 	reason.o status.o connect.o link.o offch.o ps.o cqm.o \
-+ -	bitrate.o wowlan.o coalesce.o roc.o p2p.o vendor.o
-+-+	reason.o status.o link.o offch.o ps.o cqm.o \
-+ +	bitrate.o vendor.o
-+  OBJS += sections.o
-+  

+ 11 - 20
site.mk

@@ -1,11 +1,9 @@
 GLUON_SITE_PACKAGES := \
 	gluon-mesh-batman-adv-15 \
-	ffho-ap-timer \
 	gluon-respondd \
-	ffho-ath9k-blackout-workaround \
+	ffho-ap-timer \
 	gluon-autoupdater \
 	ffho-autoupdater-wifi-fallback \
-	ffho-autoupdater-wifi-fallback-legacy \
 	ffho-banner \
 	gluon-config-mode-core \
 	gluon-config-mode-autoupdater \
@@ -17,26 +15,20 @@ GLUON_SITE_PACKAGES := \
 	ffho-debug \
 	gluon-ebtables-filter-multicast \
 	gluon-ebtables-filter-ra-dhcp \
-	ffho-ebtables-net-rules \
-	ffho-fix-defaultroute \
-	gluon-luci-admin \
-	ffho-luci-ap-timer \
-	ffho-luci-autoupdater-wifi-fallback \
-	gluon-luci-portconfig \
-	gluon-luci-wifi-config \
+	gluon-ebtables-source-filter \
 	gluon-mesh-vpn-fastd \
-	gluon-next-node \
 	gluon-radvd \
 	gluon-setup-mode \
-	ffho-site-auto-select \
-	ffho-status-page \
+	gluon-status-page \
+	gluon-web-admin \
+	ffho-web-ap-timer \
+	ffho-web-autoupdater \
+	gluon-web-logging \
+	gluon-web-network \
+	gluon-web-wifi-config \
 	iwinfo \
-	iptables \
 	haveged
 
-APU2_NIC := \
-	kmod-igb
-
 USB_BASIC := \
 	kmod-usb-core \
 	kmod-usb2 \
@@ -52,7 +44,7 @@ USB_NIC := \
 WLAN := \
 	kmod-ath9k \
 	kmod-ath10k-ct \
-	ath10k-firmware-qca988x-11s
+	ath10k-firmware-qca988x-ct
 
 ifeq ($(GLUON_TARGET),x86-generic)
 	GLUON_SITE_PACKAGES += \
@@ -64,14 +56,13 @@ endif
 
 ifeq ($(GLUON_TARGET),x86-64)
 	GLUON_SITE_PACKAGES += \
-		$(APU2_NIC) \
 		$(USB_BASIC) \
 		$(USB_NIC) \
 		$(WLAN)
 endif
 
 # 0.2~1 < 0.2 < 0.2+1 < 0.2-1 < 0.2.1 < 0.3
-DEFAULT_GLUON_RELEASE := 1.0.5~exp$(shell date '+%Y%m%d')
+DEFAULT_GLUON_RELEASE := 1.1.0~exp$(shell date '+%Y%m%d')
 DEFAULT_GLUON_PRIORITY := 0
 
 GLUON_RELEASE ?= $(DEFAULT_GLUON_RELEASE)